Build more versions
This includes Merge pull request #528 from franciscocpg/master to build arm
diff --git a/.travis.yml b/.travis.yml
index e2a6a62..0078c9c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,7 @@
go:
- 1.5
- 1.6
+ - 1.7
- tip
# Setting sudo access to false will let Travis CI use containers rather than
@@ -17,7 +18,7 @@
# in the vendor directory. We don't need to test all dependent packages.
# Only testing this project.
script:
- - GO15VENDOREXPERIMENT=1 make test
+ - GO15VENDOREXPERIMENT=1 make test integration-test
notifications:
webhooks:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd27a18..7e757f7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,28 @@
+# Unreleased
+
+## Added
+- #533: Log VCS output with debug (`--debug` switch) when there was a VCS error (thanks @atombender)
+- #39: Added support for mirrors. See the mirror command and subcommands
+
+## Changed
+- #521: Sort subpackages for glide.yaml and glide.lock to avoid spurious diffs
+- #487: Skip lookup of subpackage location when parent repo is already known
+ This skips unnecessary network requests (thanks @hori-ryota)
+- #492 and #547: Dependencies are now resolved in a global cache and exported to
+ vendor/ directories. This allows sharing of VCS data between projects without
+ upseting the GOPATH versions and is faster for projects vendoring dependencies.
+ Some flags including --update-vendored, --cache-gopath, --use-gopath, and some
+ others are deprecated and no longer needed.
+
+## Fixed
+- #553: Export was failing with different physical devices
+- #542: Glide failed to detect some test dependencies (thanks @sdboyer)
+- #517: Fixed failure to install testImport from lock when no imports present
+ or when same dependency on both import and testImport
+- #440: Fixed panic in `glide tree` when walking the filesystem (thanks @abhin4v)
+- #529: --delete flag deleted and re-downloaded transitive dependencies
+- #535: Resolve vendor directory symlinks (thanks @Fugiman)
+
# Release 0.11.1 (2016-07-21)
## Fixed
diff --git a/Makefile b/Makefile
index a33f9b4..05fcaeb 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,12 @@
install -m 755 ./glide ${DESTDIR}/usr/local/bin/glide
test:
- ${GLIDE_GO_EXECUTABLE} test . ./gb ./path ./action ./tree ./util ./godep ./godep/strip ./gpm ./cfg ./dependency ./importer ./msg ./repo
+ ${GLIDE_GO_EXECUTABLE} test . ./gb ./path ./action ./tree ./util ./godep ./godep/strip ./gpm ./cfg ./dependency ./importer ./msg ./repo ./mirrors
+
+integration-test:
+ ${GLIDE_GO_EXECUTABLE} build
+ ./glide up
+ ./glide install
clean:
rm -f ./glide.test
@@ -40,4 +45,4 @@
cd ..
-.PHONY: build test install clean bootstrap-dist build-all dist
+.PHONY: build test install clean bootstrap-dist build-all dist integration-test
diff --git a/README.md b/README.md
index 39ef0e8..ee5eae4 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@
- hg
- svn
* Support custom local and global plugins (see docs/plugins.md)
-* Repository caching including reuse of packages in the `$GOPATH`
+* Repository caching and data caching for improved performance.
* Flatten dependencies resolving version differences and avoiding the inclusion
of a package multiple times.
* Manage and install dependencies on-demand or vendored in your version control
@@ -37,16 +37,25 @@
## How It Works
-The dependencies for a project are listed in a `glide.yaml` file. This can
-include a version, VCS, repository location (that can be different from the
-package name), etc. When `glide up` is run it downloads the packages (or updates)
-to the `vendor` directory. It then recursively walks through the downloaded
-packages looking for those with a `glide.yaml` file (or Godep, gb, gom, or GPM config
-file) that don't already have a `vendor` directory and installing their
-dependencies to their `vendor` directories. Once Glide has downloaded and figured
-out versions to use in the dependency tree it creates a `glide.lock` file
-containing the complete dependency tree pinned to specific versions. To install
-the correct versions use `glide install`.
+Glide scans the source code of your application or library to determine the needed
+dependencies. To determine the versions and locations (such as aliases for forks)
+Glide reads a `glide.yaml` file with the rules. With this information Glide retrieves
+needed dependencies.
+
+When a dependent package is encountered its imports are scanned to determine
+dependencies of dependencies (transitive dependencies). If the dependent project
+contains a `glide.yaml` file that information is used to help determine the
+dependency rules when fetching from a location or version to use. Configuration
+from Godep, GB, GOM, and GPM is also imported.
+
+The dependencies are exported to the `vendor/` directory where the `go` tools
+can find and use them. A `glide.lock` file is generated containing all the
+dependencies, including transitive ones.
+
+The `glide init` command can be use to setup a new project, `glide update`
+regenerates the dependency versions using scanning and rules, and `glide install`
+will install the versions listed in the `glide.lock` file, skipping scanning,
+unless the `glide.lock` file is not found in which case it will perform an update.
A projects is structured like this:
@@ -138,23 +147,35 @@
```
$ glide create
-[INFO] Generating a YAML configuration file and guessing the dependencies
-[INFO] Attempting to import from other package managers (use --skip-import to skip)
-[INFO] Found reference to github.com/Sirupsen/logrus
-[INFO] Adding sub-package hooks/syslog to github.com/Sirupsen/logrus
-[INFO] Found reference to github.com/boltdb/bolt
-[INFO] Found reference to github.com/gorilla/websocket
-[INFO] Found reference to github.com/mndrix/ps
-[INFO] Found reference to github.com/spf13/cobra
-[INFO] Found reference to github.com/spf13/pflag
-[INFO] Found reference to github.com/tinylib/msgp/msgp
-[INFO] Found reference to github.com/unrolled/secure
-[INFO] Found reference to github.com/xeipuuv/gojsonschema
-[INFO] Found reference to github.com/zenazn/goji/graceful
-[INFO] Adding sub-package web to github.com/zenazn/goji
-[INFO] Adding sub-package web/mutil to github.com/zenazn/goji
+[INFO] Generating a YAML configuration file and guessing the dependencies
+[INFO] Attempting to import from other package managers (use --skip-import to skip)
+[INFO] Scanning code to look for dependencies
+[INFO] --> Found reference to github.com/Masterminds/semver
+[INFO] --> Found reference to github.com/Masterminds/vcs
+[INFO] --> Found reference to github.com/codegangsta/cli
+[INFO] --> Found reference to gopkg.in/yaml.v2
+[INFO] Writing configuration file (glide.yaml)
+[INFO] Would you like Glide to help you find ways to improve your glide.yaml configuration?
+[INFO] If you want to revisit this step you can use the config-wizard command at any time.
+[INFO] Yes (Y) or No (N)?
+n
+[INFO] You can now edit the glide.yaml file. Consider:
+[INFO] --> Using versions and ranges. See https://glide.sh/docs/versions/
+[INFO] --> Adding additional metadata. See https://glide.sh/docs/glide.yaml/
+[INFO] --> Running the config-wizard command to improve the versions in your configuration
```
+The `config-wizard`, noted here, can be run here or manually run at a later time.
+This wizard helps you figure out versions and ranges you can use for your
+dependencies.
+
+### glide config-wizard
+
+This runs a wizard that scans your dependencies and retrieves information on them
+to offer up suggestions that you can interactively choose. For example, it can
+discover if a dependency uses semantic versions and help you choose the version
+ranges to use.
+
### glide get [package name]
You can download one or more packages to your `vendor` directory and have it added to your
@@ -171,7 +192,7 @@
Download or update all of the libraries listed in the `glide.yaml` file and put
them in the `vendor` directory. It will also recursively walk through the
-dependency packages doing the same thing if no `vendor` directory exists.
+dependency packages to fetch anything that's needed and read in any configuration.
```
$ glide up
@@ -185,6 +206,8 @@
specified as a range (e.g., `^1.2.3`) it will be set to a specific commit id in
the `glide.lock` file. That allows for reproducible installs (see `glide install`).
+To remove any nested `vendor/` directories from fetched packages see the `-v` flag.
+
### glide install
When you want to install the specific versions from the `glide.lock` file use
@@ -204,6 +227,8 @@
If no `glide.lock` file is present `glide install` will perform an `update` and
generate a lock file.
+To remove any nested `vendor/` directories from fetched packages see the `-v` flag.
+
## glide novendor (aliased to nv)
When you run commands like `go test ./...` it will iterate over all the
@@ -223,26 +248,6 @@
the name of the package you're working on. `glide name` returns the name of the
package listed in the `glide.yaml` file.
-### glide rebuild
-
-Runs `go install` on the packages in the `glide.yaml` file. This
-(along with `glide install` and `glide update`) pays special attention
-to the contents of the `subpackages:` directive in the YAML file.
-
-```
-$ glide rebuild
-[INFO] Building dependencies.
-[INFO] Running go build github.com/kylelemons/go-gypsy/yaml
-[INFO] Running go build github.com/Masterminds/cookoo/cli
-[INFO] Running go build github.com/Masterminds/cookoo
-```
-
-This is useful when you are working with large 3rd party libraries. It
-will create the `.a` files, which can have a positive impact on your
-build times.
-
-**This feature is deprecated and will be removed before Glide 1.0.0**
-
### glide tree
Glide includes a few commands that inspect code and give you details
@@ -289,6 +294,8 @@
places, `glide tree` also prints the location of the package being
imported.
+_This command is deprecated and will be removed in the near future._
+
### glide list
Glide's `list` command shows an alphabetized list of all the packages
@@ -321,11 +328,13 @@
```
$ glide --version
-glide version 0.8.0
+glide version 0.12.0
```
### glide.yaml
+For full details on the `glide.yaml` files see [the documentation](https://glide.sh/docs/glide.yaml).
+
The `glide.yaml` file does two critical things:
1. It names the current package
@@ -338,7 +347,6 @@
import:
- package: github.com/Masterminds/semver
- package: github.com/Masterminds/cookoo
- vcs: git
version: ^1.2.0
repo: git@github.com:Masterminds/cookoo.git
```
@@ -348,7 +356,6 @@
1. This package is named `github.com/Masterminds/glide`
2. That this package depends on two libraries.
-
The first library exemplifies a minimal package import. It merely gives
the fully qualified import path.
diff --git a/action/cache.go b/action/cache.go
index 9896f21..b2ee07a 100644
--- a/action/cache.go
+++ b/action/cache.go
@@ -9,21 +9,15 @@
// CacheClear clears the Glide cache
func CacheClear() {
- l, err := cache.Location()
- if err != nil {
- msg.Die("Unable to clear the cache: %s", err)
- }
+ l := cache.Location()
- err = os.RemoveAll(l)
+ err := os.RemoveAll(l)
if err != nil {
msg.Die("Unable to clear the cache: %s", err)
}
cache.SetupReset()
- err = cache.Setup()
- if err != nil {
- msg.Die("Unable to clear the cache: %s", err)
- }
+ cache.Setup()
msg.Info("Glide cache has been cleared.")
}
diff --git a/action/config_wizard.go b/action/config_wizard.go
index 1257980..ae2b317 100644
--- a/action/config_wizard.go
+++ b/action/config_wizard.go
@@ -18,6 +18,7 @@
// ConfigWizard reads configuration from a glide.yaml file and attempts to suggest
// improvements. The wizard is interactive.
func ConfigWizard(base string) {
+ cache.SystemLock()
_, err := gpath.Glide()
glidefile := gpath.GlideFile
if err != nil {
@@ -37,10 +38,7 @@
conf := EnsureConfig()
- err = cache.Setup()
- if err != nil {
- msg.Die("Problem setting up cache: %s", err)
- }
+ cache.Setup()
msg.Info("Looking for dependencies to make suggestions on")
msg.Info("--> Scanning for dependencies not using version ranges")
@@ -67,12 +65,7 @@
var changes int
for _, dep := range deps {
- var remote string
- if dep.Repository != "" {
- remote = dep.Repository
- } else {
- remote = "https://" + dep.Name
- }
+ remote := dep.Remote()
// First check, ask if the tag should be used instead of the commit id for it.
cur := cache.MemCurrent(remote)
@@ -257,17 +250,8 @@
var createGitParseVersion = regexp.MustCompile(`(?m-s)(?:tags)/(\S+)$`)
func wizardFindVersions(d *cfg.Dependency) {
- l, err := cache.Location()
- if err != nil {
- msg.Debug("Problem detecting cache location: %s", err)
- return
- }
- var remote string
- if d.Repository != "" {
- remote = d.Repository
- } else {
- remote = "https://" + d.Name
- }
+ l := cache.Location()
+ remote := d.Remote()
key, err := cache.Key(remote)
if err != nil {
diff --git a/action/ensure.go b/action/ensure.go
index 2f5f5af..2933c1c 100644
--- a/action/ensure.go
+++ b/action/ensure.go
@@ -9,6 +9,7 @@
"strings"
"github.com/Masterminds/glide/cfg"
+ "github.com/Masterminds/glide/mirrors"
"github.com/Masterminds/glide/msg"
gpath "github.com/Masterminds/glide/path"
"github.com/Masterminds/glide/util"
@@ -56,6 +57,11 @@
}
}
+ err = mirrors.Load()
+ if err != nil {
+ msg.Err("Unable to load mirrors: %s", err)
+ }
+
return conf
}
diff --git a/action/get.go b/action/get.go
index 5c34d5e..696c50c 100644
--- a/action/get.go
+++ b/action/get.go
@@ -18,10 +18,8 @@
// Get fetches one or more dependencies and installs.
//
// This includes resolving dependency resolution and re-generating the lock file.
-func Get(names []string, installer *repo.Installer, insecure, skipRecursive, strip, stripVendor, nonInteract, testDeps bool) {
- if installer.UseCache {
- cache.SystemLock()
- }
+func Get(names []string, installer *repo.Installer, insecure, skipRecursive, stripVendor, nonInteract, testDeps bool) {
+ cache.SystemLock()
base := gpath.Basepath()
EnsureGopath()
@@ -72,11 +70,9 @@
msg.Err("Failed to set references: %s", err)
}
- // VendoredCleanup
- // When stripping VCS happens this will happen as well. No need for double
- // effort.
- if installer.UpdateVendored && !strip {
- repo.VendoredCleanup(confcopy)
+ err = installer.Export(confcopy)
+ if err != nil {
+ msg.Die("Unable to export dependencies to vendor directory: %s", err)
}
// Write YAML
@@ -93,14 +89,6 @@
msg.Warn("Skipping lockfile generation because full dependency tree is not being calculated")
}
- if strip {
- msg.Info("Removing version control data from vendor directory...")
- err := gpath.StripVcs()
- if err != nil {
- msg.Err("Unable to strip version control data: %s", err)
- }
- }
-
if stripVendor {
msg.Info("Removing nested vendor and Godeps/_workspace directories...")
err := gpath.StripVendor()
@@ -115,7 +103,10 @@
if err != nil {
msg.Die("Failed to generate config hash. Unable to generate lock file.")
}
- lock := cfg.NewLockfile(confcopy.Imports, confcopy.DevImports, hash)
+ lock, err := cfg.NewLockfile(confcopy.Imports, confcopy.DevImports, hash)
+ if err != nil {
+ msg.Die("Failed to generate lock file: %s", err)
+ }
if err := lock.WriteFile(filepath.Join(base, gpath.LockFile)); err != nil {
msg.Die("Failed to write glide lock file: %s", err)
}
@@ -234,12 +225,7 @@
}
func getWizard(dep *cfg.Dependency) {
- var remote string
- if dep.Repository != "" {
- remote = dep.Repository
- } else {
- remote = "https://" + dep.Name
- }
+ remote := dep.Remote()
// Lookup dependency info and store in cache.
msg.Info("--> Gathering release information for %s", dep.Name)
diff --git a/action/install.go b/action/install.go
index ee91401..75eaeb2 100644
--- a/action/install.go
+++ b/action/install.go
@@ -5,17 +5,14 @@
"github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/cfg"
- "github.com/Masterminds/glide/dependency"
"github.com/Masterminds/glide/msg"
gpath "github.com/Masterminds/glide/path"
"github.com/Masterminds/glide/repo"
)
// Install installs a vendor directory based on an existing Glide configuration.
-func Install(installer *repo.Installer, strip, stripVendor bool) {
- if installer.UseCache {
- cache.SystemLock()
- }
+func Install(installer *repo.Installer, stripVendor bool) {
+ cache.SystemLock()
base := "."
// Ensure GOPATH
@@ -26,7 +23,7 @@
// Lockfile exists
if !gpath.HasLock(base) {
msg.Info("Lock file (glide.lock) does not exist. Performing update.")
- Update(installer, false, strip, stripVendor)
+ Update(installer, false, stripVendor)
return
}
// Load lockfile
@@ -42,14 +39,6 @@
msg.Warn("Lock file may be out of date. Hash check of YAML failed. You may need to run 'update'")
}
- // Delete unused packages
- if installer.DeleteUnused {
- // It's unclear whether this should operate off of the lock, or off
- // of the glide.yaml file. I'd think that doing this based on the
- // lock would be much more reliable.
- dependency.DeleteUnused(conf)
- }
-
// Install
newConf, err := installer.Install(lock, conf)
if err != nil {
@@ -60,22 +49,12 @@
// Set reference
if err := repo.SetReference(newConf, installer.ResolveTest); err != nil {
- msg.Err("Failed to set references: %s (Skip to cleanup)", err)
+ msg.Die("Failed to set references: %s (Skip to cleanup)", err)
}
- // VendoredCleanup. This should ONLY be run if UpdateVendored was specified.
- // When stripping VCS happens this will happen as well. No need for double
- // effort.
- if installer.UpdateVendored && !strip {
- repo.VendoredCleanup(newConf)
- }
-
- if strip {
- msg.Info("Removing version control data from vendor directory...")
- err := gpath.StripVcs()
- if err != nil {
- msg.Err("Unable to strip version control data: %s", err)
- }
+ err = installer.Export(newConf)
+ if err != nil {
+ msg.Die("Unable to export dependencies to vendor directory: %s", err)
}
if stripVendor {
diff --git a/action/list.go b/action/list.go
index f4b3379..29d815f 100644
--- a/action/list.go
+++ b/action/list.go
@@ -25,7 +25,7 @@
if err != nil {
msg.Die("Could not create a resolver: %s", err)
}
- h := &dependency.DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}}
+ h := &dependency.DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}, Prefix: "vendor"}
r.Handler = h
localPkgs, _, err := r.ResolveLocal(deep)
diff --git a/action/mirrors.go b/action/mirrors.go
new file mode 100644
index 0000000..edb3bed
--- /dev/null
+++ b/action/mirrors.go
@@ -0,0 +1,146 @@
+package action
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/Masterminds/glide/mirrors"
+ "github.com/Masterminds/glide/msg"
+ gpath "github.com/Masterminds/glide/path"
+)
+
+// MirrorsList displays a list of currently setup mirrors.
+func MirrorsList() error {
+ home := gpath.Home()
+
+ op := filepath.Join(home, "mirrors.yaml")
+
+ if _, err := os.Stat(op); os.IsNotExist(err) {
+ msg.Info("No mirrors exist. No mirrors.yaml file not found")
+ return nil
+ }
+
+ ov, err := mirrors.ReadMirrorsFile(op)
+ if err != nil {
+ msg.Die("Unable to read mirrors.yaml file: %s", err)
+ }
+
+ if len(ov.Repos) == 0 {
+ msg.Info("No mirrors found")
+ return nil
+ }
+
+ msg.Info("Mirrors...")
+ for _, r := range ov.Repos {
+ if r.Vcs == "" {
+ msg.Info("--> %s replaced by %s", r.Original, r.Repo)
+ } else {
+ msg.Info("--> %s replaced by %s (%s)", r.Original, r.Repo, r.Vcs)
+ }
+ }
+
+ return nil
+}
+
+// MirrorsSet sets a mirror to use
+func MirrorsSet(o, r, v string) error {
+ if o == "" || r == "" {
+ msg.Err("Both the original and mirror values are required")
+ return nil
+ }
+
+ home := gpath.Home()
+
+ op := filepath.Join(home, "mirrors.yaml")
+
+ var ov *mirrors.Mirrors
+ if _, err := os.Stat(op); os.IsNotExist(err) {
+ msg.Info("No mirrors.yaml file exists. Creating new one")
+ ov = &mirrors.Mirrors{
+ Repos: make(mirrors.MirrorRepos, 0),
+ }
+ } else {
+ ov, err = mirrors.ReadMirrorsFile(op)
+ if err != nil {
+ msg.Die("Error reading existing mirrors.yaml file: %s", err)
+ }
+ }
+
+ found := false
+ for i, re := range ov.Repos {
+ if re.Original == o {
+ found = true
+ msg.Info("%s found in mirrors. Replacing with new settings", o)
+ ov.Repos[i].Repo = r
+ ov.Repos[i].Vcs = v
+ }
+ }
+
+ if !found {
+ nr := &mirrors.MirrorRepo{
+ Original: o,
+ Repo: r,
+ Vcs: v,
+ }
+ ov.Repos = append(ov.Repos, nr)
+ }
+
+ msg.Info("%s being set to %s", o, r)
+
+ err := ov.WriteFile(op)
+ if err != nil {
+ msg.Err("Error writing mirrors.yaml file: %s", err)
+ } else {
+ msg.Info("mirrors.yaml written with changes")
+ }
+
+ return nil
+}
+
+// MirrorsRemove removes a mirrors setting
+func MirrorsRemove(k string) error {
+ if k == "" {
+ msg.Err("The mirror to remove is required")
+ return nil
+ }
+
+ home := gpath.Home()
+
+ op := filepath.Join(home, "mirrors.yaml")
+
+ if _, err := os.Stat(op); os.IsNotExist(err) {
+ msg.Err("mirrors.yaml file not found")
+ return nil
+ }
+
+ ov, err := mirrors.ReadMirrorsFile(op)
+ if err != nil {
+ msg.Die("Unable to read mirrors.yaml file: %s", err)
+ }
+
+ var nre mirrors.MirrorRepos
+ var found bool
+ for _, re := range ov.Repos {
+ if re.Original != k {
+ nre = append(nre, re)
+ } else {
+ found = true
+ }
+ }
+
+ if !found {
+ msg.Warn("%s was not found in mirrors", k)
+ } else {
+ msg.Info("%s was removed from mirrors", k)
+ ov.Repos = nre
+
+ err = ov.WriteFile(op)
+ if err != nil {
+ msg.Err("Error writing mirrors.yaml file: %s", err)
+ } else {
+ msg.Info("mirrors.yaml written with changes")
+ }
+ }
+
+ return nil
+}
diff --git a/action/remove.go b/action/remove.go
index fc305f5..63a6a58 100644
--- a/action/remove.go
+++ b/action/remove.go
@@ -1,6 +1,7 @@
package action
import (
+ "github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/msg"
gpath "github.com/Masterminds/glide/path"
@@ -9,6 +10,7 @@
// Remove removes a dependncy from the configuration.
func Remove(packages []string, inst *repo.Installer) {
+ cache.SystemLock()
base := gpath.Basepath()
EnsureGopath()
EnsureVendorDir()
@@ -25,16 +27,15 @@
// Copy used to generate locks.
confcopy := conf.Clone()
- confcopy.Imports = inst.List(confcopy)
+ //confcopy.Imports = inst.List(confcopy)
if err := repo.SetReference(confcopy, inst.ResolveTest); err != nil {
msg.Err("Failed to set references: %s", err)
}
- // TODO: Right now, there is no flag to enable this, so this will never be
- // run. I am not sure whether we should allow this in a rm op or not.
- if inst.UpdateVendored {
- repo.VendoredCleanup(confcopy)
+ err = inst.Export(confcopy)
+ if err != nil {
+ msg.Die("Unable to export dependencies to vendor directory: %s", err)
}
// Write glide.yaml
diff --git a/action/update.go b/action/update.go
index fdd90ad..19ede20 100644
--- a/action/update.go
+++ b/action/update.go
@@ -6,29 +6,20 @@
"github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/cfg"
- "github.com/Masterminds/glide/dependency"
- "github.com/Masterminds/glide/godep"
"github.com/Masterminds/glide/msg"
gpath "github.com/Masterminds/glide/path"
"github.com/Masterminds/glide/repo"
)
// Update updates repos and the lock file from the main glide yaml.
-func Update(installer *repo.Installer, skipRecursive, strip, stripVendor bool) {
- if installer.UseCache {
- cache.SystemLock()
- }
+func Update(installer *repo.Installer, skipRecursive, stripVendor bool) {
+ cache.SystemLock()
base := "."
EnsureGopath()
EnsureVendorDir()
conf := EnsureConfig()
- // Delete unused packages
- if installer.DeleteUnused {
- dependency.DeleteUnused(conf)
- }
-
// Try to check out the initial dependencies.
if err := installer.Checkout(conf); err != nil {
msg.Die("Failed to do initial checkout of config: %s", err)
@@ -59,12 +50,10 @@
msg.Err("Failed to set references: %s (Skip to cleanup)", err)
}
}
- // Vendored cleanup
- // VendoredCleanup. This should ONLY be run if UpdateVendored was specified.
- // When stripping VCS happens this will happen as well. No need for double
- // effort.
- if installer.UpdateVendored && !strip {
- repo.VendoredCleanup(confcopy)
+
+ err := installer.Export(confcopy)
+ if err != nil {
+ msg.Die("Unable to export dependencies to vendor directory: %s", err)
}
// Write glide.yaml (Why? Godeps/GPM/GB?)
@@ -76,17 +65,16 @@
// from the project. A removed dependency should warn and an added dependency
// should be added to the glide.yaml file. See issue #193.
- if stripVendor {
- confcopy = godep.RemoveGodepSubpackages(confcopy)
- }
-
if !skipRecursive {
// Write lock
hash, err := conf.Hash()
if err != nil {
msg.Die("Failed to generate config hash. Unable to generate lock file.")
}
- lock := cfg.NewLockfile(confcopy.Imports, confcopy.DevImports, hash)
+ lock, err := cfg.NewLockfile(confcopy.Imports, confcopy.DevImports, hash)
+ if err != nil {
+ msg.Die("Failed to generate lock file: %s", err)
+ }
wl := true
if gpath.HasLock(base) {
yml, err := ioutil.ReadFile(filepath.Join(base, gpath.LockFile))
@@ -115,14 +103,6 @@
msg.Warn("Skipping lockfile generation because full dependency tree is not being calculated")
}
- if strip {
- msg.Info("Removing version control data from vendor directory...")
- err := gpath.StripVcs()
- if err != nil {
- msg.Err("Unable to strip version control data: %s", err)
- }
- }
-
if stripVendor {
msg.Info("Removing nested vendor and Godeps/_workspace directories...")
err := gpath.StripVendor()
diff --git a/cache/cache.go b/cache/cache.go
index 5a8538d..21f4edd 100644
--- a/cache/cache.go
+++ b/cache/cache.go
@@ -54,12 +54,12 @@
var setupMutex sync.Mutex
// Setup creates the cache location.
-func Setup() error {
+func Setup() {
setupMutex.Lock()
defer setupMutex.Unlock()
if isSetup {
- return nil
+ return
}
msg.Debug("Setting up the cache directory")
pths := []string{
@@ -71,12 +71,11 @@
for _, l := range pths {
err := os.MkdirAll(filepath.Join(gpath.Home(), l), 0755)
if err != nil {
- return err
+ msg.Die("Cache directory unavailable: %s", err)
}
}
isSetup = true
- return nil
}
// SetupReset resets if setup has been completed. The next time setup is run
@@ -86,11 +85,11 @@
}
// Location returns the location of the cache.
-func Location() (string, error) {
+func Location() string {
p := filepath.Join(gpath.Home(), "cache")
- err := Setup()
+ Setup()
- return p, err
+ return p
}
// scpSyntaxRe matches the SCP-like addresses used to access repos over SSH.
@@ -153,10 +152,7 @@
if !Enabled {
return ErrCacheDisabled
}
- location, err := Location()
- if err != nil {
- return err
- }
+ location := Location()
data.LastUpdate = time.Now().String()
d, err := json.Marshal(data)
if err != nil {
@@ -185,10 +181,7 @@
if !Enabled {
return &RepoInfo{}, ErrCacheDisabled
}
- location, err := Location()
- if err != nil {
- return &RepoInfo{}, err
- }
+ location := Location()
c := &RepoInfo{}
p := filepath.Join(location, "info", key+".json")
f, err := ioutil.ReadFile(p)
diff --git a/cfg/config.go b/cfg/config.go
index 4b4e799..ae01039 100644
--- a/cfg/config.go
+++ b/cfg/config.go
@@ -5,8 +5,10 @@
"fmt"
"io/ioutil"
"reflect"
+ "sort"
"strings"
+ "github.com/Masterminds/glide/mirrors"
"github.com/Masterminds/glide/util"
"github.com/Masterminds/vcs"
"gopkg.in/yaml.v2"
@@ -367,15 +369,14 @@
// Dependency describes a package that the present package depends upon.
type Dependency struct {
- Name string `yaml:"package"`
- Reference string `yaml:"version,omitempty"`
- Pin string `yaml:"-"`
- Repository string `yaml:"repo,omitempty"`
- VcsType string `yaml:"vcs,omitempty"`
- Subpackages []string `yaml:"subpackages,omitempty"`
- Arch []string `yaml:"arch,omitempty"`
- Os []string `yaml:"os,omitempty"`
- UpdateAsVendored bool `yaml:"-"`
+ Name string `yaml:"package"`
+ Reference string `yaml:"version,omitempty"`
+ Pin string `yaml:"-"`
+ Repository string `yaml:"repo,omitempty"`
+ VcsType string `yaml:"vcs,omitempty"`
+ Subpackages []string `yaml:"subpackages,omitempty"`
+ Arch []string `yaml:"arch,omitempty"`
+ Os []string `yaml:"os,omitempty"`
}
// A transitive representation of a dependency for importing and exploting to yaml.
@@ -459,22 +460,56 @@
return newDep, nil
}
+// Remote returns the remote location to fetch source from. This location is
+// the central place where mirrors can alter the location.
+func (d *Dependency) Remote() string {
+ var r string
+
+ if d.Repository != "" {
+ r = d.Repository
+ } else {
+ r = "https://" + d.Name
+ }
+
+ f, nr, _ := mirrors.Get(r)
+ if f {
+ return nr
+ }
+
+ return r
+}
+
+// Vcs returns the VCS type to fetch source from.
+func (d *Dependency) Vcs() string {
+ var r string
+
+ if d.Repository != "" {
+ r = d.Repository
+ } else {
+ r = "https://" + d.Name
+ }
+
+ f, _, nv := mirrors.Get(r)
+ if f {
+ return nv
+ }
+
+ return d.VcsType
+}
+
// GetRepo retrieves a Masterminds/vcs repo object configured for the root
// of the package being retrieved.
func (d *Dependency) GetRepo(dest string) (vcs.Repo, error) {
// The remote location is either the configured repo or the package
// name as an https url.
- var remote string
- if len(d.Repository) > 0 {
- remote = d.Repository
- } else {
- remote = "https://" + d.Name
- }
+ remote := d.Remote()
+
+ VcsType := d.Vcs()
// If the VCS type has a value we try that first.
- if len(d.VcsType) > 0 && d.VcsType != "None" {
- switch vcs.Type(d.VcsType) {
+ if len(VcsType) > 0 && VcsType != "None" {
+ switch vcs.Type(VcsType) {
case vcs.Git:
return vcs.NewGitRepo(remote, dest)
case vcs.Svn:
@@ -484,7 +519,7 @@
case vcs.Bzr:
return vcs.NewBzrRepo(remote, dest)
default:
- return nil, fmt.Errorf("Unknown VCS type %s set for %s", d.VcsType, d.Name)
+ return nil, fmt.Errorf("Unknown VCS type %s set for %s", VcsType, d.Name)
}
}
@@ -495,15 +530,14 @@
// Clone creates a clone of a Dependency
func (d *Dependency) Clone() *Dependency {
return &Dependency{
- Name: d.Name,
- Reference: d.Reference,
- Pin: d.Pin,
- Repository: d.Repository,
- VcsType: d.VcsType,
- Subpackages: d.Subpackages,
- Arch: d.Arch,
- Os: d.Os,
- UpdateAsVendored: d.UpdateAsVendored,
+ Name: d.Name,
+ Reference: d.Reference,
+ Pin: d.Pin,
+ Repository: d.Repository,
+ VcsType: d.VcsType,
+ Subpackages: d.Subpackages,
+ Arch: d.Arch,
+ Os: d.Os,
}
}
@@ -567,6 +601,7 @@
s = append(s, item)
}
}
+ sort.Strings(s)
return s
}
diff --git a/cfg/lock.go b/cfg/lock.go
index 215483c..c03ac41 100644
--- a/cfg/lock.go
+++ b/cfg/lock.go
@@ -2,6 +2,7 @@
import (
"crypto/sha256"
+ "fmt"
"io/ioutil"
"sort"
"strings"
@@ -34,6 +35,39 @@
return yml, nil
}
+// MarshalYAML is a hook for gopkg.in/yaml.v2.
+// It sorts import subpackages lexicographically for reproducibility.
+func (lf *Lockfile) MarshalYAML() (interface{}, error) {
+ for _, imp := range lf.Imports {
+ sort.Strings(imp.Subpackages)
+ }
+
+ // Ensure elements on testImport don't already exist on import.
+ var newDI Locks
+ var found bool
+ for _, imp := range lf.DevImports {
+ found = false
+ for i := 0; i < len(lf.Imports); i++ {
+ if lf.Imports[i].Name == imp.Name {
+ found = true
+ if lf.Imports[i].Version != imp.Version {
+ return lf, fmt.Errorf("Generating lock YAML produced conflicting versions of %s. import (%s), testImport (%s)", imp.Name, lf.Imports[i].Version, imp.Version)
+ }
+ }
+ }
+
+ if !found {
+ newDI = append(newDI, imp)
+ }
+ }
+ lf.DevImports = newDI
+
+ for _, imp := range lf.DevImports {
+ sort.Strings(imp.Subpackages)
+ }
+ return lf, nil
+}
+
// WriteFile writes a Glide lock file.
//
// This is a convenience function that marshals the YAML and then writes it to
@@ -157,12 +191,12 @@
}
// NewLockfile is used to create an instance of Lockfile.
-func NewLockfile(ds, tds Dependencies, hash string) *Lockfile {
+func NewLockfile(ds, tds Dependencies, hash string) (*Lockfile, error) {
lf := &Lockfile{
Hash: hash,
Updated: time.Now(),
Imports: make([]*Lock, len(ds)),
- DevImports: make([]*Lock, len(tds)),
+ DevImports: make([]*Lock, 0),
}
for i := 0; i < len(ds); i++ {
@@ -171,13 +205,26 @@
sort.Sort(lf.Imports)
+ var found bool
for i := 0; i < len(tds); i++ {
- lf.DevImports[i] = LockFromDependency(tds[i])
+ found = false
+ for ii := 0; ii < len(ds); ii++ {
+ if ds[ii].Name == tds[i].Name {
+ found = true
+ if ds[ii].Reference != tds[i].Reference {
+ return &Lockfile{}, fmt.Errorf("Generating lock produced conflicting versions of %s. import (%s), testImport (%s)", tds[i].Name, ds[ii].Reference, tds[i].Reference)
+ }
+ break
+ }
+ }
+ if !found {
+ lf.DevImports = append(lf.DevImports, LockFromDependency(tds[i]))
+ }
}
sort.Sort(lf.DevImports)
- return lf
+ return lf, nil
}
// LockfileFromMap takes a map of dependencies and generates a lock Lockfile instance.
diff --git a/cfg/lock_test.go b/cfg/lock_test.go
index 78dcee3..b6bb31e 100644
--- a/cfg/lock_test.go
+++ b/cfg/lock_test.go
@@ -2,6 +2,7 @@
import (
"sort"
+ "strings"
"testing"
)
@@ -32,3 +33,80 @@
t.Error("Sorting of dependencies failed")
}
}
+
+const inputSubpkgYaml = `
+imports:
+- name: github.com/gogo/protobuf
+ version: 82d16f734d6d871204a3feb1a73cb220cc92574c
+ subpackages:
+ - plugin/equal
+ - sortkeys
+ - plugin/face
+ - plugin/gostring
+ - vanity
+ - plugin/grpc
+ - plugin/marshalto
+ - plugin/populate
+ - plugin/oneofcheck
+ - plugin/size
+ - plugin/stringer
+ - plugin/defaultcheck
+ - plugin/embedcheck
+ - plugin/description
+ - plugin/enumstringer
+ - gogoproto
+ - plugin/testgen
+ - plugin/union
+ - plugin/unmarshal
+ - protoc-gen-gogo/generator
+ - protoc-gen-gogo/plugin
+ - vanity/command
+ - protoc-gen-gogo/descriptor
+ - proto
+`
+const expectSubpkgYaml = `
+imports:
+- name: github.com/gogo/protobuf
+ version: 82d16f734d6d871204a3feb1a73cb220cc92574c
+ subpackages:
+ - gogoproto
+ - plugin/defaultcheck
+ - plugin/description
+ - plugin/embedcheck
+ - plugin/enumstringer
+ - plugin/equal
+ - plugin/face
+ - plugin/gostring
+ - plugin/grpc
+ - plugin/marshalto
+ - plugin/oneofcheck
+ - plugin/populate
+ - plugin/size
+ - plugin/stringer
+ - plugin/testgen
+ - plugin/union
+ - plugin/unmarshal
+ - proto
+ - protoc-gen-gogo/descriptor
+ - protoc-gen-gogo/generator
+ - protoc-gen-gogo/plugin
+ - sortkeys
+ - vanity
+ - vanity/command
+`
+
+func TestSortSubpackages(t *testing.T) {
+ lf, err := LockfileFromYaml([]byte(inputSubpkgYaml))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ out, err := lf.Marshal()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !strings.Contains(string(out), expectSubpkgYaml) {
+ t.Errorf("Expected %q\n to contain\n%q", string(out), expectSubpkgYaml)
+ }
+}
diff --git a/dependency/delete.go b/dependency/delete.go
deleted file mode 100644
index 39a6367..0000000
--- a/dependency/delete.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package dependency
-
-import (
- "errors"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/Masterminds/glide/cfg"
- "github.com/Masterminds/glide/msg"
- gpath "github.com/Masterminds/glide/path"
-)
-
-// DeleteUnused removes packages from vendor/ that are no longer used.
-//
-// TODO: This should work off of a Lock file, not glide.yaml.
-func DeleteUnused(conf *cfg.Config) error {
- vpath, err := gpath.Vendor()
- if err != nil {
- return err
- }
- if vpath == "" {
- return errors.New("Vendor not set")
- }
-
- // Build directory tree of what to keep.
- var pkgList []string
- for _, dep := range conf.Imports {
- pkgList = append(pkgList, dep.Name)
- }
-
- var searchPath string
- var markForDelete []string
- // Callback function for filepath.Walk to delete packages not in yaml file.
- fn := func(path string, info os.FileInfo, err error) error {
- // Bubble up the error
- if err != nil {
- return err
- }
-
- if info.IsDir() == false || path == searchPath || path == vpath {
- return nil
- }
-
- localPath := strings.TrimPrefix(path, searchPath)
- keep := false
-
- // First check if the path has a prefix that's a specific package. If
- // so we keep it to keep the package.
- for _, name := range pkgList {
- if strings.HasPrefix(localPath, name) {
- keep = true
- }
- }
-
- // If a package is, for example, github.com/Masterminds/glide the
- // previous look will not mark the directories github.com or
- // github.com/Masterminds to keep. Here we see if these names prefix
- // and packages we know about to mark as keepers.
- if keep == false {
- for _, name := range pkgList {
- if strings.HasPrefix(name, localPath) {
- keep = true
- }
- }
- }
-
- // If the parent directory has already been marked for delete this
- // directory doesn't need to be marked.
- for _, markedDirectory := range markForDelete {
- if strings.HasPrefix(path, markedDirectory) {
- return nil
- }
- }
-
- // Remove the directory if we are not keeping it.
- if keep == false {
- // Mark for deletion
- markForDelete = append(markForDelete, path)
- }
-
- return nil
- }
-
- // Walk vendor directory
- searchPath = vpath + string(os.PathSeparator)
- err = filepath.Walk(searchPath, fn)
- if err != nil {
- return err
- }
-
- // Perform the actual delete.
- for _, path := range markForDelete {
- localPath := strings.TrimPrefix(path, searchPath)
- msg.Info("Removing unused package: %s\n", localPath)
- rerr := os.RemoveAll(path)
- if rerr != nil {
- return rerr
- }
- }
-
- return nil
-}
diff --git a/dependency/resolver.go b/dependency/resolver.go
index 06dd2dc..e237564 100644
--- a/dependency/resolver.go
+++ b/dependency/resolver.go
@@ -2,7 +2,9 @@
import (
"container/list"
+ "errors"
"runtime"
+ "sort"
//"go/build"
"os"
"path/filepath"
@@ -51,6 +53,10 @@
//
// This can be used update a project found in the vendor/ folder.
InVendor(pkg string, addTest bool) error
+
+ // PkgPath is called to find the location locally to scan. This gives the
+ // handler to do things such as use a cached location.
+ PkgPath(pkg string) string
}
// DefaultMissingPackageHandler is the default handler for missing packages.
@@ -60,6 +66,7 @@
type DefaultMissingPackageHandler struct {
Missing []string
Gopath []string
+ Prefix string
}
// NotFound prints a warning and then stores the package name in Missing.
@@ -84,6 +91,14 @@
return nil
}
+// PkgPath returns the path to the package
+func (d *DefaultMissingPackageHandler) PkgPath(pkg string) string {
+ if d.Prefix != "" {
+ return filepath.Join(d.Prefix, pkg)
+ }
+ return pkg
+}
+
// VersionHandler sets the version for a package when found while scanning.
//
// When a package if found it needs to be on the correct version before
@@ -290,7 +305,7 @@
}
} else {
imps = p.Imports
- testImps = p.TestImports
+ testImps = dedupeStrings(p.TestImports, p.XTestImports)
}
// We are only looking for dependencies in vendor. No root, cgo, etc.
@@ -470,7 +485,7 @@
// Here, we want to import the package and see what imports it has.
msg.Debug("Trying to open %s", vdep)
var imps []string
- pkg, err := r.BuildContext.ImportDir(vdep, 0)
+ pkg, err := r.BuildContext.ImportDir(r.Handler.PkgPath(dep), 0)
if err != nil && strings.HasPrefix(err.Error(), "found packages ") {
// If we got here it's because a package and multiple packages
// declared. This is often because of an example with a package
@@ -478,9 +493,9 @@
// try to brute force the packages with a slower scan.
msg.Debug("Using Iterative Scanning for %s", dep)
if testDeps {
- _, imps, err = IterativeScan(vdep)
+ _, imps, err = IterativeScan(r.Handler.PkgPath(dep))
} else {
- imps, _, err = IterativeScan(vdep)
+ imps, _, err = IterativeScan(r.Handler.PkgPath(dep))
}
if err != nil {
@@ -488,8 +503,9 @@
continue
}
} else if err != nil {
- msg.Debug("ImportDir error on %s: %s", vdep, err)
- if strings.HasPrefix(err.Error(), "no buildable Go source") {
+ errStr := err.Error()
+ msg.Debug("ImportDir error on %s: %s", r.Handler.PkgPath(dep), err)
+ if strings.HasPrefix(errStr, "no buildable Go source") {
msg.Debug("No subpackages declared. Skipping %s.", dep)
continue
} else if os.IsNotExist(err) && !foundErr && !foundQ {
@@ -516,6 +532,16 @@
// see if this is on GOPATH and copy it?
msg.Info("Not found in vendor/: %s (1)", dep)
}
+ } else if strings.Contains(errStr, "no such file or directory") {
+ r.hadError[dep] = true
+ msg.Err("Error scanning %s: %s", dep, err)
+ msg.Err("This error means the referenced package was not found.")
+ msg.Err("Missing file or directory errors usually occur when multiple packages")
+ msg.Err("share a common dependency and the first reference encountered by the scanner")
+ msg.Err("sets the version to one that does not contain a subpackage needed required")
+ msg.Err("by another package that uses the shared dependency. Try setting a")
+ msg.Err("version in your glide.yaml that works for all packages that share this")
+ msg.Err("dependency.")
} else {
r.hadError[dep] = true
msg.Err("Error scanning %s: %s", dep, err)
@@ -523,7 +549,7 @@
continue
} else {
if testDeps {
- imps = pkg.TestImports
+ imps = dedupeStrings(pkg.TestImports, pkg.XTestImports)
} else {
imps = pkg.Imports
}
@@ -562,10 +588,10 @@
r.VersionHandler.SetVersion(imp, addTest)
} else if err != nil {
r.hadError[dep] = true
- msg.Warn("Error looking for %s: %s", imp, err)
+ msg.Err("Error looking for %s: %s", imp, err)
} else {
r.hadError[dep] = true
- msg.Info("Not found: %s (2)", imp)
+ msg.Err("Not found: %s (2)", imp)
}
case LocGopath:
msg.Debug("Found on GOPATH, not vendor: %s", imp)
@@ -582,6 +608,11 @@
}
+ if len(r.hadError) > 0 {
+ // Errors occurred so we return.
+ return []string{}, errors.New("Error resolving imports")
+ }
+
// FIXME: From here to the end is a straight copy of the resolveList() func.
res := make([]string, 0, queue.Len())
@@ -767,16 +798,16 @@
// FIXME: On error this should try to NotFound to the dependency, and then import
// it again.
var imps []string
- p, err := r.BuildContext.ImportDir(pkg, 0)
+ p, err := r.BuildContext.ImportDir(r.Handler.PkgPath(pkg), 0)
if err != nil && strings.HasPrefix(err.Error(), "found packages ") {
// If we got here it's because a package and multiple packages
// declared. This is often because of an example with a package
// or main but +build ignore as a build tag. In that case we
// try to brute force the packages with a slower scan.
if testDeps {
- _, imps, err = IterativeScan(pkg)
+ _, imps, err = IterativeScan(r.Handler.PkgPath(pkg))
} else {
- imps, _, err = IterativeScan(pkg)
+ imps, _, err = IterativeScan(r.Handler.PkgPath(pkg))
}
if err != nil {
@@ -786,7 +817,7 @@
return []string{}, err
} else {
if testDeps {
- imps = p.TestImports
+ imps = dedupeStrings(p.TestImports, p.XTestImports)
} else {
imps = p.Imports
}
@@ -1065,3 +1096,29 @@
return basedir, nil
}
+
+// helper func to merge, dedupe, and sort strings
+func dedupeStrings(s1, s2 []string) (r []string) {
+ dedupe := make(map[string]bool)
+
+ if len(s1) > 0 && len(s2) > 0 {
+ for _, i := range s1 {
+ dedupe[i] = true
+ }
+ for _, i := range s2 {
+ dedupe[i] = true
+ }
+
+ for i := range dedupe {
+ r = append(r, i)
+ }
+ // And then re-sort them
+ sort.Strings(r)
+ } else if len(s1) > 0 {
+ r = s1
+ } else if len(s2) > 0 {
+ r = s2
+ }
+
+ return
+}
diff --git a/dependency/resolver_test.go b/dependency/resolver_test.go
index 1867cee..f4b44d9 100644
--- a/dependency/resolver_test.go
+++ b/dependency/resolver_test.go
@@ -46,6 +46,8 @@
if err != nil {
t.Fatal(err)
}
+ h := &DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}, Prefix: "../vendor"}
+ r.Handler = h
l, _, err := r.ResolveLocal(true)
if err != nil {
@@ -62,6 +64,8 @@
if err != nil {
t.Fatal(err)
}
+ h := &DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}, Prefix: "../vendor"}
+ r.Handler = h
base := filepath.Join(os.Getenv("GOPATH"), "src/github.com/Masterminds/glide/vendor")
l, err := r.Resolve("github.com/codegangsta/cli", base)
@@ -91,6 +95,8 @@
if err != nil {
t.Fatalf("No new resolver: %s", err)
}
+ h := &DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}, Prefix: "../vendor"}
+ r.Handler = h
l, err := r.ResolveAll(deps, false)
if err != nil {
t.Fatalf("Failed to resolve: %s", err)
diff --git a/dependency/scan.go b/dependency/scan.go
index 71f4865..a8794d1 100644
--- a/dependency/scan.go
+++ b/dependency/scan.go
@@ -137,6 +137,18 @@
testPkgs = append(testPkgs, dep)
}
}
+
+ for _, dep := range pk.XTestImports {
+ found := false
+ for _, p := range pkgs {
+ if p == dep {
+ found = true
+ }
+ }
+ if !found {
+ testPkgs = append(testPkgs, dep)
+ }
+ }
}
return pkgs, testPkgs, nil
diff --git a/docs/commands.md b/docs/commands.md
index 3b233db..0449bc5 100644
--- a/docs/commands.md
+++ b/docs/commands.md
@@ -4,25 +4,40 @@
## glide create (aliased to init)
-Initializes a new workspace. Among other things, this creates a `glide.yaml` file while attempting to guess the packages and versions to put in it. For example, if your project is using Godep it will use the versions specified there. Glide is smart enough to scan your codebase and detect the imports being used whether they are specified with another package manager or not.
+Initialize a new workspace. Among other things, this creates a `glide.yaml` file
+while attempting to guess the packages and versions to put in it. For example,
+if your project is using Godep it will use the versions specified there. Glide
+is smart enough to scan your codebase and detect the imports being used whether
+they are specified with another package manager or not.
- $ glide init
- [INFO] Generating a YAML configuration file and guessing the dependencies
- [INFO] Attempting to import from other package managers (use --skip-import to skip)
- [INFO] Found reference to github.com/BurntSushi/toml
- [INFO] Found reference to github.com/Masterminds/semver
- [INFO] Found reference to github.com/Masterminds/sprig
- [INFO] Found reference to github.com/Masterminds/vcs
- [INFO] Found reference to github.com/aokoli/goutils
- [INFO] Found reference to github.com/codegangsta/cli
- [INFO] Found reference to github.com/deis/pkg/prettyprint
- [INFO] Found reference to github.com/ghodss/yaml
- [INFO] Found reference to github.com/google/go-github/github
- [INFO] Found reference to github.com/pborman/uuid
- [INFO] Found reference to golang.org/x/crypto/nacl/box
- [INFO] Adding sub-package ssh/terminal to golang.org/x/crypto
- [INFO] Found reference to gopkg.in/yaml.v2
- ...
+ $ glide create
+ [INFO] Generating a YAML configuration file and guessing the dependencies
+ [INFO] Attempting to import from other package managers (use --skip-import to skip)
+ [INFO] Scanning code to look for dependencies
+ [INFO] --> Found reference to github.com/Masterminds/semver
+ [INFO] --> Found reference to github.com/Masterminds/vcs
+ [INFO] --> Found reference to github.com/codegangsta/cli
+ [INFO] --> Found reference to gopkg.in/yaml.v2
+ [INFO] Writing configuration file (glide.yaml)
+ [INFO] Would you like Glide to help you find ways to improve your glide.yaml configuration?
+ [INFO] If you want to revisit this step you can use the config-wizard command at any time.
+ [INFO] Yes (Y) or No (N)?
+ n
+ [INFO] You can now edit the glide.yaml file. Consider:
+ [INFO] --> Using versions and ranges. See https://glide.sh/docs/versions/
+ [INFO] --> Adding additional metadata. See https://glide.sh/docs/glide.yaml/
+ [INFO] --> Running the config-wizard command to improve the versions in your configuration
+
+The `config-wizard`, noted here, can be run here or manually run at a later time.
+This wizard helps you figure out versions and ranges you can use for your
+dependencies.
+
+### glide config-wizard
+
+This runs a wizard that scans your dependencies and retrieves information on them
+to offer up suggestions that you can interactively choose. For example, it can
+discover if a dependency uses semantic versions and help you choose the version
+ranges to use.
## glide get [package name]
@@ -37,29 +52,25 @@
$ glide get github.com/Masterminds/cookoo#^1.2.3
-The version is separated from the package name by an anchor (`#`).
+The version is separated from the package name by an anchor (`#`). If no version or range is specified and the dependency uses Semantic Versions Glide will prompt you to ask if you want to use them.
## glide update (aliased to up)
-Download or update all of the libraries listed in the `glide.yaml` file and put them in the `vendor` directory. It will also recursively walk through the dependency packages doing the same thing if no `vendor` directory exists.
+Download or update all of the libraries listed in the `glide.yaml` file and put
+them in the `vendor` directory. It will also recursively walk through the
+dependency packages to fetch anything that's needed and read in any configuration.
$ glide up
-This will recurse over the packages looking for other projects managed by Glide, Godep, GB, Gom, and GPM. When one is found those packages will be installed as needed.
+This will recurse over the packages looking for other projects managed by Glide,
+Godep, gb, gom, and GPM. When one is found those packages will be installed as needed.
-A `glide.lock` file will be created or updated with the dependencies pinned to specific versions. For example, if in the `glide.yaml` file a version was specified as a range (e.g., `^1.2.3`) it will be set to a specific commit id in the `glide.lock` file. That allows for reproducible installs (see `glide install`).
+A `glide.lock` file will be created or updated with the dependencies pinned to
+specific versions. For example, if in the `glide.yaml` file a version was
+specified as a range (e.g., `^1.2.3`) it will be set to a specific commit id in
+the `glide.lock` file. That allows for reproducible installs (see `glide install`).
-If you want to use `glide up` to help you managed dependencies that are checked into your version control consider the flags:
-
-* `--update-vendored` (aliased to `-u`) to update the vendored dependencies. If Glide detects a vendored dependency it will update it and leave it in a vendored state. Note, any tertiary dependencies will not be automatically vendored with this flag.
-* `--strip-vcs` (aliased to `-s`) to strip VCS metadata (e.g., `.git` directories) from the `vendor` folder.
-* `--strip-vendor` (aliased to `-v`) to strip nested `vendor/` directories.
-
-For example, you can use the command:
-
- $ glide up -u -s
-
-This will tell Glide to update the vendored packages and remove any VCS directories from transitive dependencies that were picked up as well.
+To remove any nested `vendor/` directories from fetched packages see the `-v` flag.
## glide install
@@ -73,6 +84,8 @@
If no `glide.lock` file is present `glide install` will perform an `update` and generates a lock file.
+To remove any nested `vendor/` directories from fetched packages see the `-v` flag.
+
## glide novendor (aliased to nv)
When you run commands like `go test ./...` it will iterate over all the subdirectories including the `vendor` directory. When you are testing your application you may want to test your application files without running all the tests of your dependencies and their dependencies. This is where the `novendor` command comes in. It lists all of the directories except `vendor`.
@@ -89,32 +102,59 @@
Glide's `list` command shows an alphabetized list of all the packages that a project imports.
-```
-$ glide list
-INSTALLED packages:
- vendor/github.com/Masterminds/cookoo
- vendor/github.com/Masterminds/cookoo/fmt
- vendor/github.com/Masterminds/cookoo/io
- vendor/github.com/Masterminds/cookoo/web
- vendor/github.com/Masterminds/semver
- vendor/github.com/Masterminds/vcs
- vendor/github.com/codegangsta/cli
- vendor/gopkg.in/yaml.v2
-```
+ $ glide list
+ INSTALLED packages:
+ vendor/github.com/Masterminds/cookoo
+ vendor/github.com/Masterminds/cookoo/fmt
+ vendor/github.com/Masterminds/cookoo/io
+ vendor/github.com/Masterminds/cookoo/web
+ vendor/github.com/Masterminds/semver
+ vendor/github.com/Masterminds/vcs
+ vendor/github.com/codegangsta/cli
+ vendor/gopkg.in/yaml.v2
## glide help
Print the glide help.
-```
-$ glide help
-```
+ $ glide help
## glide --version
Print the version and exit.
-```
-$ glide --version
-glide version 0.9.0
-```
+ $ glide --version
+ glide version 0.12.0
+
+## glide mirror
+
+Mirrors provide the ability to replace a repo location with
+another location that's a mirror of the original. This is useful when you want
+to have a cache for your continuous integration (CI) system or if you want to
+work on a dependency in a local location.
+
+The mirrors are stored in an `mirrors.yaml` file in your `GLIDE_HOME`.
+
+The three commands to manager mirrors are `list`, `set`, and `remove`.
+
+Use `set` in the form:
+
+ glide mirror set [original] [replacement]
+
+or
+
+ glide mirror set [original] [replacement] --vcs [type]
+
+for example,
+
+ glide mirror set https://github.com/example/foo https://git.example.com/example/foo.git
+
+ glide mirror set https://github.com/example/foo file:///path/to/local/repo --vcs git
+
+Use `remove` in the form:
+
+ glide mirror remove [original]
+
+for example,
+
+ glide mirror remove https://github.com/example/foo
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 7d2a7c6..26c9fa7 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -10,6 +10,8 @@
When this is complete you'll have a `glide.yaml` file populated with the projects being used. You can open up this file and even edit it to add information such as versions.
+Running `glide init` will also ask if you would like to use a wizard to discover information about your dependencies versions and use versions or ranges. Each decision is interactive and your choice.
+
## Updating Dependencies
To fetch the dependencies and set them to any versions specified in the `glide.yaml` file use the following command:
@@ -22,6 +24,8 @@
Glide will then create a `glide.lock` file. This file contains the entire dependency tree pinned to specific commit ids. This file, as we'll see in a moment, can be used to recreate the exact dependency tree and versions used.
+If you want to remove nested `vendor/` directories from within dependencies use the `--strip-vendor` or `-v` flag.
+
### Dependency Flattening
All of the dependencies Glide fetches are into the top level `vendor/` folder for a project. Go provides the ability for each package to have a `vendor/` folder. Glide only uses a top level folder for two reasons:
@@ -29,6 +33,8 @@
1. Each import location will be compiled into the binary. If the same dependency is imported into three `vendor/` folders it will be in the compiled binary tree times. This can quickly lead to binary bloat.
2. Instances of types created in a dependency in one `vendor/` folder are not compatible with the same dependency in other locations. Even if they are the same version. Go sees them as different packages because they are in different locations. This is a problem for database drivers, loggers, and many other things. If you [try to pass an instance created from one location of a package to another you'll encounter errors](https://github.com/mattfarina/golang-broken-vendor).
+If a dependency has a `vendor/` directory of its own Glide does not remove it by default. The resolution in the `go` toolchain will use these nested versions if they are present. To remove them use the `--strip-vendor` or `-v` flag on the `up` or `install` commands.
+
## Installing Dependencies
If you want to install the dependencies needed by a project use the `install` command like so:
@@ -55,3 +61,5 @@
$ glide get github.com/Masterminds/semver#~1.2.0
The `#` is used as a separator between the dependency name and a version to use. The version can be a semantic version, version range, branch, tag, or commit id.
+
+If no version or range is specified and the dependency uses Semantic Versions Glide will prompt you to ask if you want to use them.
diff --git a/docs/index.md b/docs/index.md
index 00419e8..c9791bf 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -16,6 +16,7 @@
There are a few ways to install Glide.
-1. Download a [versioned release](https://github.com/Masterminds/glide/releases). Glide releases are semantically versioned.
-2. Use a system package manager to install Glide. For example, using `brew install glide` can be used if you're using [Homebrew](http://brew.sh) on Mac.
-3. The latest development snapshot can be installed with `go get`. For example, `go get -u github.com/Masterminds/glide`. This is not a release version.
+1. Use the shell script to try an automatically install it. `curl https://glide.sh/get | sh`
+2. Download a [versioned release](https://github.com/Masterminds/glide/releases). Glide releases are semantically versioned.
+3. Use a system package manager to install Glide. For example, using `brew install glide` can be used if you're using [Homebrew](http://brew.sh) on Mac.
+4. The latest development snapshot can be installed with `go get`. For example, `go get -u github.com/Masterminds/glide`. This is not a release version.
diff --git a/glide.go b/glide.go
index 1190885..0c34030 100644
--- a/glide.go
+++ b/glide.go
@@ -1,39 +1,21 @@
-// Glide is a command line utility that manages Go project dependencies and
-// your GOPATH.
+// Glide is a command line utility that manages Go project dependencies.
//
-// Dependencies are managed via a glide.yaml in the root of a project. The yaml
-//
-// Params:
-// - filename (string): The name of the glide YAML file. Default is glide.yaml.
-// - project (string): The name of the project. Default is 'main'.
-// file lets you specify projects, versions (tags, branches, or references),
-// and even alias one location in as other one. Aliasing is useful when supporting
-// forks without needing to rewrite the imports in a codebase.
+// Configureation of where to start is managed via a glide.yaml in the root of a
+// project. The yaml
//
// A glide.yaml file looks like:
//
// package: github.com/Masterminds/glide
// imports:
-// - package: github.com/Masterminds/cookoo
-// vcs: git
-// ref: 1.1.0
-// subpackages: **
-// - package: github.com/kylelemons/go-gypsy
-// subpackages: yaml
+// - package: github.com/Masterminds/cookoo
+// - package: github.com/kylelemons/go-gypsy
+// subpackages:
+// - yaml
//
// Glide puts dependencies in a vendor directory. Go utilities require this to
-// be in your GOPATH. Glide makes this easy. Use the `glide in` command to enter
-// a shell (your default) with the GOPATH set to the projects vendor directory.
-// To leave this shell simply exit it.
+// be in your GOPATH. Glide makes this easy.
//
-// If your .bashrc, .zshrc, or other startup shell sets your GOPATH you many need
-// to optionally set it using something like:
-//
-// if [ "" = "${GOPATH}" ]; then
-// export GOPATH="/some/dir"
-// fi
-//
-// For more information use the `glide help` command or see https://github.com/Masterminds/glide
+// For more information use the `glide help` command or see https://glide.sh
package main
import (
@@ -52,25 +34,23 @@
"os"
)
-var version = "0.11.0-dev"
+var version = "0.12.0-dev"
-const usage = `The lightweight vendor package manager for your Go projects.
+const usage = `Vendor Package Management for your Go projects.
-Each project should have a 'glide.yaml' file in the project directory. Files
-look something like this:
+ Each project should have a 'glide.yaml' file in the project directory. Files
+ look something like this:
- package: github.com/Masterminds/glide
- imports:
- - package: github.com/Masterminds/cookoo
- vcs: git
- ref: 1.1.0
- subpackages: **
- - package: github.com/kylelemons/go-gypsy
- subpackages: yaml
- flatten: true
+ package: github.com/Masterminds/glide
+ imports:
+ - package: github.com/Masterminds/cookoo
+ version: 1.1.0
+ - package: github.com/kylelemons/go-gypsy
+ subpackages:
+ - yaml
-NOTE: As of Glide 0.5, the commands 'into', 'gopath', 'status', and 'env'
-no longer exist.
+ For more details on the 'glide.yaml' files see the documentation at
+ https://glide.sh/docs/glide.yaml
`
// VendorDir default vendor directory name
@@ -92,8 +72,9 @@
Usage: "Quiet (no info or debug messages)",
},
cli.BoolFlag{
- Name: "verbose",
- Usage: "Print detailed informational messages",
+ Name: "verbose",
+ Usage: "Print detailed informational messages",
+ Hidden: true,
},
cli.BoolFlag{
Name: "debug",
@@ -105,6 +86,12 @@
Usage: "The location of Glide files",
EnvVar: "GLIDE_HOME",
},
+ cli.StringFlag{
+ Name: "tmp",
+ Value: "",
+ Usage: "The temp directory to use. Defaults to systems temp",
+ EnvVar: "GLIDE_TMP",
+ },
cli.BoolFlag{
Name: "no-color",
Usage: "Turn off colored output for log messages",
@@ -155,8 +142,9 @@
Usage: "Disable interactive prompts.",
},
},
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.Create(".", c.Bool("skip-import"), c.Bool("non-interactive"))
+ return nil
},
},
{
@@ -166,8 +154,9 @@
Description: `Glide will analyze a projects glide.yaml file and the imported
projects to find ways the glide.yaml file can potentially be improved. It
will then interactively make suggestions that you can skip or accept.`,
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.ConfigWizard(".")
+ return nil
},
},
{
@@ -176,27 +165,21 @@
Description: `Gets one or more package (like 'go get') and then adds that file
to the glide.yaml file. Multiple package names can be specified on one line.
- $ glide get github.com/Masterminds/cookoo/web
+ $ glide get github.com/Masterminds/cookoo/web
The above will install the project github.com/Masterminds/cookoo and add
the subpackage 'web'.
If a fetched dependency has a glide.yaml file, configuration from Godep,
- GPM, or GB Glide that configuration will be used to find the dependencies
+ GPM, GOM, or GB Glide that configuration will be used to find the dependencies
and versions to fetch. If those are not available the dependent packages will
be fetched as either a version specified elsewhere or the latest version.
When adding a new dependency Glide will perform an update to work out the
- the versions to use from the dependency tree. This will generate an updated
- glide.lock file with specific locked versions to use.
+ the versions for the dependencies of this dependency (transitive ones). This
+ will generate an updated glide.lock file with specific locked versions to use.
- If you are storing the outside dependencies in your version control system
- (VCS), also known as vendoring, there are a few flags that may be useful.
- The '--update-vendored' flag will cause Glide to update packages when VCS
- information is unavailable. This can be used with the '--strip-vcs' flag which
- will strip VCS data found in the vendor directory. This is useful for
- removing VCS data from transitive dependencies and initial setups. The
- '--strip-vendor' flag will remove any nested 'vendor' folders and
+ The '--strip-vendor' flag will remove any nested 'vendor' folders and
'Godeps/_workspace' folders after an update (along with undoing any Godep
import rewriting). Note, The Godeps specific functionality is deprecated and
will be removed when most Godeps users have migrated to using the vendor
@@ -223,32 +206,37 @@
Usage: "This will resolve all dependencies for all packages, not just those directly used.",
},
cli.BoolFlag{
- Name: "update-vendored, u",
- Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.",
+ Name: "update-vendored, u",
+ Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.",
+ Hidden: true,
},
cli.BoolFlag{
- Name: "cache",
- Usage: "When downloading dependencies attempt to cache them.",
+ Name: "cache",
+ Usage: "When downloading dependencies attempt to cache them.",
+ Hidden: true,
},
cli.BoolFlag{
- Name: "cache-gopath",
- Usage: "When downloading dependencies attempt to put them in the GOPATH, too.",
+ Name: "cache-gopath",
+ Usage: "When downloading dependencies attempt to put them in the GOPATH, too.",
+ Hidden: true,
},
cli.BoolFlag{
- Name: "use-gopath",
- Usage: "Copy dependencies from the GOPATH if they exist there.",
+ Name: "use-gopath",
+ Usage: "Copy dependencies from the GOPATH if they exist there.",
+ Hidden: true,
},
cli.BoolFlag{
Name: "resolve-current",
Usage: "Resolve dependencies for only the current system rather than all build modes.",
},
cli.BoolFlag{
- Name: "strip-vcs, s",
- Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
+ Name: "strip-vcs, s",
+ Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
+ Hidden: true,
},
cli.BoolFlag{
Name: "strip-vendor, v",
- Usage: "Removes nested vendor and Godeps/_workspace directories. Requires --strip-vcs.",
+ Usage: "Removes nested vendor and Godeps/_workspace directories.",
},
cli.BoolFlag{
Name: "non-interactive",
@@ -259,9 +247,27 @@
Usage: "Resolve dependencies in test files.",
},
},
- Action: func(c *cli.Context) {
- if c.Bool("strip-vendor") && !c.Bool("strip-vcs") {
- msg.Die("--strip-vendor cannot be used without --strip-vcs")
+ Action: func(c *cli.Context) error {
+ if c.Bool("delete") {
+ msg.Warn("The --delete flag is deprecated. This now works by default.")
+ }
+ if c.Bool("update-vendored") {
+ msg.Warn("The --update-vendored flag is deprecated. This now works by default.")
+ }
+ if c.String("file") != "" {
+ msg.Warn("The --flag flag is deprecated.")
+ }
+ if c.Bool("cache") {
+ msg.Warn("The --cache flag is deprecated. This now works by default.")
+ }
+ if c.Bool("cache-gopath") {
+ msg.Warn("The --cache-gopath flag is deprecated.")
+ }
+ if c.Bool("use-gopath") {
+ msg.Warn("The --use-gopath flag is deprecated. Please see overrides.")
+ }
+ if c.Bool("strip-vcs") {
+ msg.Warn("The --strip-vcs flag is deprecated. This now works by default.")
}
if len(c.Args()) < 1 {
@@ -276,15 +282,12 @@
inst := repo.NewInstaller()
inst.Force = c.Bool("force")
- inst.UseCache = c.Bool("cache")
- inst.UseGopath = c.Bool("use-gopath")
- inst.UseCacheGopath = c.Bool("cache-gopath")
- inst.UpdateVendored = c.Bool("update-vendored")
inst.ResolveAllFiles = c.Bool("all-dependencies")
inst.ResolveTest = !c.Bool("skip-test")
packages := []string(c.Args())
insecure := c.Bool("insecure")
- action.Get(packages, inst, insecure, c.Bool("no-recursive"), c.Bool("strip-vcs"), c.Bool("strip-vendor"), c.Bool("non-interactive"), c.Bool("test"))
+ action.Get(packages, inst, insecure, c.Bool("no-recursive"), c.Bool("strip-vendor"), c.Bool("non-interactive"), c.Bool("test"))
+ return nil
},
},
{
@@ -292,18 +295,14 @@
ShortName: "rm",
Usage: "Remove a package from the glide.yaml file, and regenerate the lock file.",
Description: `This takes one or more package names, and removes references from the glide.yaml file.
- This will rebuild the glide lock file with the following constraints:
-
- - Dependencies are re-negotiated. Any that are no longer used are left out of the lock.
- - Minor version re-nogotiation is performed on remaining dependencies.
- - No updates are peformed. You may want to run 'glide up' to accomplish that.`,
+ This will rebuild the glide lock file re-resolving the depencies.`,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "delete,d",
Usage: "Also delete from vendor/ any packages that are no longer used.",
},
},
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
if len(c.Args()) < 1 {
fmt.Println("Oops! At least one package name is required.")
os.Exit(1)
@@ -317,6 +316,7 @@
inst.Force = c.Bool("force")
packages := []string(c.Args())
action.Remove(packages, inst)
+ return nil
},
},
{
@@ -332,8 +332,9 @@
Usage: "Save all of the discovered dependencies to a Glide YAML file.",
},
},
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.ImportGodep(c.String("file"))
+ return nil
},
},
{
@@ -345,8 +346,9 @@
Usage: "Save all of the discovered dependencies to a Glide YAML file.",
},
},
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.ImportGPM(c.String("file"))
+ return nil
},
},
{
@@ -358,8 +360,9 @@
Usage: "Save all of the discovered dependencies to a Glide YAML file.",
},
},
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.ImportGB(c.String("file"))
+ return nil
},
},
{
@@ -371,8 +374,9 @@
Usage: "Save all of the discovered dependencies to a Glide YAML file.",
},
},
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.ImportGom(c.String("file"))
+ return nil
},
},
},
@@ -381,8 +385,9 @@
Name: "name",
Usage: "Print the name of this project.",
Description: `Read the glide.yaml file and print the name given on the 'package' line.`,
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.Name()
+ return nil
},
},
{
@@ -404,17 +409,19 @@
Usage: "Specify this to prevent nv from append '/...' to all directories.",
},
},
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.NoVendor(c.String("dir"), true, !c.Bool("no-subdir"))
+ return nil
},
},
{
Name: "rebuild",
Usage: "Rebuild ('go build') the dependencies",
- Description: `This rebuilds the packages' '.a' files. On some systems
+ Description: `(Deprecated) This rebuilds the packages' '.a' files. On some systems
this can improve performance on subsequent 'go run' and 'go build' calls.`,
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.Rebuild()
+ return nil
},
},
{
@@ -430,108 +437,115 @@
from the lock file.`,
Flags: []cli.Flag{
cli.BoolFlag{
- Name: "delete",
- Usage: "Delete vendor packages not specified in config.",
+ Name: "delete",
+ Usage: "Delete vendor packages not specified in config.",
+ Hidden: true,
},
cli.BoolFlag{
Name: "force",
Usage: "If there was a change in the repo or VCS switch to new one. Warning: changes will be lost.",
},
cli.BoolFlag{
- Name: "update-vendored, u",
- Usage: "Update vendored packages (without local VCS repo). Warning: this may destroy local modifications to vendor/.",
+ Name: "update-vendored, u",
+ Usage: "Update vendored packages (without local VCS repo). Warning: this may destroy local modifications to vendor/.",
+ Hidden: true,
},
cli.StringFlag{
- Name: "file, f",
- Usage: "Save all of the discovered dependencies to a Glide YAML file. (DEPRECATED: This has no impact.)",
+ Name: "file, f",
+ Usage: "Save all of the discovered dependencies to a Glide YAML file. (DEPRECATED: This has no impact.)",
+ Hidden: true,
},
cli.BoolFlag{
- Name: "cache",
- Usage: "When downloading dependencies attempt to cache them.",
+ Name: "cache",
+ Usage: "When downloading dependencies attempt to cache them.",
+ Hidden: true,
},
cli.BoolFlag{
- Name: "cache-gopath",
- Usage: "When downloading dependencies attempt to put them in the GOPATH, too.",
+ Name: "cache-gopath",
+ Usage: "When downloading dependencies attempt to put them in the GOPATH, too.",
+ Hidden: true,
},
cli.BoolFlag{
- Name: "use-gopath",
- Usage: "Copy dependencies from the GOPATH if they exist there.",
+ Name: "use-gopath",
+ Usage: "Copy dependencies from the GOPATH if they exist there.",
+ Hidden: true,
},
cli.BoolFlag{
- Name: "strip-vcs, s",
- Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
+ Name: "strip-vcs, s",
+ Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
+ Hidden: true,
},
cli.BoolFlag{
Name: "strip-vendor, v",
- Usage: "Removes nested vendor and Godeps/_workspace directories. Requires --strip-vcs.",
+ Usage: "Removes nested vendor and Godeps/_workspace directories.",
},
cli.BoolFlag{
Name: "skip-test",
Usage: "Resolve dependencies in test files.",
},
},
- Action: func(c *cli.Context) {
- if c.Bool("strip-vendor") && !c.Bool("strip-vcs") {
- msg.Die("--strip-vendor cannot be used without --strip-vcs")
+ Action: func(c *cli.Context) error {
+ if c.Bool("delete") {
+ msg.Warn("The --delete flag is deprecated. This now works by default.")
+ }
+ if c.Bool("update-vendored") {
+ msg.Warn("The --update-vendored flag is deprecated. This now works by default.")
+ }
+ if c.String("file") != "" {
+ msg.Warn("The --flag flag is deprecated.")
+ }
+ if c.Bool("cache") {
+ msg.Warn("The --cache flag is deprecated. This now works by default.")
+ }
+ if c.Bool("cache-gopath") {
+ msg.Warn("The --cache-gopath flag is deprecated.")
+ }
+ if c.Bool("use-gopath") {
+ msg.Warn("The --use-gopath flag is deprecated. Please see overrides.")
+ }
+ if c.Bool("strip-vcs") {
+ msg.Warn("The --strip-vcs flag is deprecated. This now works by default.")
}
installer := repo.NewInstaller()
installer.Force = c.Bool("force")
- installer.UseCache = c.Bool("cache")
- installer.UseGopath = c.Bool("use-gopath")
- installer.UseCacheGopath = c.Bool("cache-gopath")
- installer.UpdateVendored = c.Bool("update-vendored")
installer.Home = c.GlobalString("home")
- installer.DeleteUnused = c.Bool("delete")
installer.ResolveTest = !c.Bool("skip-test")
- action.Install(installer, c.Bool("strip-vcs"), c.Bool("strip-vendor"))
+ action.Install(installer, c.Bool("strip-vendor"))
+ return nil
},
},
{
Name: "update",
ShortName: "up",
Usage: "Update a project's dependencies",
- Description: `This uses the native VCS of each package to try to
- pull the most applicable updates. Packages with fixed refs (Versions or
- tags) will not be updated. Packages with no ref or with a branch ref will
- be updated as expected.
+ Description: `This updates the dependencies by scanning the codebase
+ to determine the needed dependencies and fetching them following the rules
+ in the glide.yaml file. When no rules exist the tip of the default branch
+ is used. For more details see https://glide.sh/docs/glide.yaml
If a dependency has a glide.yaml file, update will read that file and
- update those dependencies accordingly. Those dependencies are maintained in
+ use the information contained there. Those dependencies are maintained in
a the top level 'vendor/' directory. 'vendor/foo/bar' will have its
dependencies stored in 'vendor/'. This behavior can be disabled with
'--no-recursive'. When this behavior is skipped a glide.lock file is not
generated because the full dependency tree cannot be known.
- Glide will also import Godep, GB, and GPM files as it finds them in dependencies.
+ Glide will also import Godep, GB, GOM, and GPM files as it finds them in dependencies.
It will create a glide.yaml file from the Godeps data, and then update. This
has no effect if '--no-recursive' is set.
- If you are storing the outside dependencies in your version control system
- (VCS), also known as vendoring, there are a few flags that may be useful.
- The '--update-vendored' flag will cause Glide to update packages when VCS
- information is unavailable. This can be used with the '--strip-vcs' flag which
- will strip VCS data found in the vendor directory. This is useful for
- removing VCS data from transitive dependencies and initial setups. The
- '--strip-vendor' flag will remove any nested 'vendor' folders and
+ The '--strip-vendor' flag will remove any nested 'vendor' folders and
'Godeps/_workspace' folders after an update (along with undoing any Godep
import rewriting). Note, The Godeps specific functionality is deprecated and
will be removed when most Godeps users have migrated to using the vendor
- folder.
-
- Note, Glide detects vendored dependencies. With the '--update-vendored' flag
- Glide will update vendored dependencies leaving them in a vendored state.
- Tertiary dependencies will not be vendored automatically unless the
- '--strip-vcs' flag is used along with it.
-
- By default, packages that are discovered are considered transient, and are
- not stored in the glide.yaml file. The --file=NAME.yaml flag allows you
- to save the discovered dependencies to a YAML file.`,
+ folder.`,
Flags: []cli.Flag{
cli.BoolFlag{
- Name: "delete",
- Usage: "Delete vendor packages not specified in config.",
+ Name: "delete",
+ Usage: "Delete vendor packages not specified in config.",
+ Hidden: true,
},
cli.BoolFlag{
Name: "no-recursive, quick",
@@ -546,45 +560,69 @@
Usage: "This will resolve all dependencies for all packages, not just those directly used.",
},
cli.BoolFlag{
- Name: "update-vendored, u",
- Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.",
+ Name: "update-vendored, u",
+ Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.",
+ Hidden: true,
},
cli.StringFlag{
- Name: "file, f",
- Usage: "Save all of the discovered dependencies to a Glide YAML file.",
+ Name: "file, f",
+ Usage: "Save all of the discovered dependencies to a Glide YAML file.",
+ Hidden: true,
},
cli.BoolFlag{
- Name: "cache",
- Usage: "When downloading dependencies attempt to cache them.",
+ Name: "cache",
+ Usage: "When downloading dependencies attempt to cache them.",
+ Hidden: true,
},
cli.BoolFlag{
- Name: "cache-gopath",
- Usage: "When downloading dependencies attempt to put them in the GOPATH, too.",
+ Name: "cache-gopath",
+ Usage: "When downloading dependencies attempt to put them in the GOPATH, too.",
+ Hidden: true,
},
cli.BoolFlag{
- Name: "use-gopath",
- Usage: "Copy dependencies from the GOPATH if they exist there.",
+ Name: "use-gopath",
+ Usage: "Copy dependencies from the GOPATH if they exist there.",
+ Hidden: true,
},
cli.BoolFlag{
Name: "resolve-current",
Usage: "Resolve dependencies for only the current system rather than all build modes.",
},
cli.BoolFlag{
- Name: "strip-vcs, s",
- Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
+ Name: "strip-vcs, s",
+ Usage: "Removes version control metadata (e.g, .git directory) from the vendor folder.",
+ Hidden: true,
},
cli.BoolFlag{
Name: "strip-vendor, v",
- Usage: "Removes nested vendor and Godeps/_workspace directories. Requires --strip-vcs.",
+ Usage: "Removes nested vendor and Godeps/_workspace directories.",
},
cli.BoolFlag{
Name: "skip-test",
Usage: "Resolve dependencies in test files.",
},
},
- Action: func(c *cli.Context) {
- if c.Bool("strip-vendor") && !c.Bool("strip-vcs") {
- msg.Die("--strip-vendor cannot be used without --strip-vcs")
+ Action: func(c *cli.Context) error {
+ if c.Bool("delete") {
+ msg.Warn("The --delete flag is deprecated. This now works by default.")
+ }
+ if c.Bool("update-vendored") {
+ msg.Warn("The --update-vendored flag is deprecated. This now works by default.")
+ }
+ if c.String("file") != "" {
+ msg.Warn("The --flag flag is deprecated.")
+ }
+ if c.Bool("cache") {
+ msg.Warn("The --cache flag is deprecated. This now works by default.")
+ }
+ if c.Bool("cache-gopath") {
+ msg.Warn("The --cache-gopath flag is deprecated.")
+ }
+ if c.Bool("use-gopath") {
+ msg.Warn("The --use-gopath flag is deprecated. Please see overrides.")
+ }
+ if c.Bool("strip-vcs") {
+ msg.Warn("The --strip-vcs flag is deprecated. This now works by default.")
}
if c.Bool("resolve-current") {
@@ -594,29 +632,30 @@
installer := repo.NewInstaller()
installer.Force = c.Bool("force")
- installer.UseCache = c.Bool("cache")
- installer.UseGopath = c.Bool("use-gopath")
- installer.UseCacheGopath = c.Bool("cache-gopath")
- installer.UpdateVendored = c.Bool("update-vendored")
installer.ResolveAllFiles = c.Bool("all-dependencies")
installer.Home = c.GlobalString("home")
- installer.DeleteUnused = c.Bool("delete")
installer.ResolveTest = !c.Bool("skip-test")
- action.Update(installer, c.Bool("no-recursive"), c.Bool("strip-vcs"), c.Bool("strip-vendor"))
+ action.Update(installer, c.Bool("no-recursive"), c.Bool("strip-vendor"))
+
+ return nil
},
},
{
Name: "tree",
- Usage: "Tree prints the dependencies of this project as a tree.",
+ Usage: "(Deprecated) Tree prints the dependencies of this project as a tree.",
Description: `This scans a project's source files and builds a tree
representation of the import graph.
It ignores testdata/ and directories that begin with . or _. Packages in
vendor/ are only included if they are referenced by the main project or
- one of its dependencies.`,
- Action: func(c *cli.Context) {
+ one of its dependencies.
+
+ Note, for large projects this can display a large list tens of thousands of
+ lines long.`,
+ Action: func(c *cli.Context) error {
action.Tree(".", false)
+ return nil
},
},
{
@@ -629,8 +668,9 @@
Directories that begin with . or _ are ignored, as are testdata directories. Packages in
vendor are only included if they are used by the project.`,
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.List(".", true, c.String("output"))
+ return nil
},
Flags: []cli.Flag{
cli.StringFlag{
@@ -674,27 +714,114 @@
glide info -f "%n - %d - %h - %l"
prints 'foo - Some example description - https://example.com - MIT'`,
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
if c.IsSet("format") {
action.Info(c.String("format"))
} else {
cli.ShowCommandHelp(c, c.Command.Name)
}
+ return nil
},
},
{
Name: "cache-clear",
ShortName: "cc",
Usage: "Clears the Glide cache.",
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.CacheClear()
+ return nil
},
},
{
Name: "about",
Usage: "Learn about Glide",
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
action.About()
+ return nil
+ },
+ },
+ {
+ Name: "mirror",
+ Usage: "Manage mirrors",
+ Description: `Mirrors provide the ability to replace a repo location with
+ another location that's a mirror of the original. This is useful when you want
+ to have a cache for your continuous integration (CI) system or if you want to
+ work on a dependency in a local location.
+
+ The mirrors are stored in an mirrors.yaml file in your GLIDE_HOME.
+
+ The three commands to manager mirrors are 'list', 'set', and 'remove'.
+
+ Use 'set' in the form:
+
+ glide mirror set [original] [replacement]
+
+ or
+
+ glide mirror set [original] [replacement] --vcs [type]
+
+ for example,
+
+ glide mirror set https://github.com/example/foo https://git.example.com/example/foo.git
+
+ glide mirror set https://github.com/example/foo file:///path/to/local/repo --vcs git
+
+ Use 'remove' in the form:
+
+ glide mirror remove [original]
+
+ for example,
+
+ glide mirror remove https://github.com/example/foo`,
+ Subcommands: []cli.Command{
+ {
+ Name: "list",
+ Usage: "List the current mirrors",
+ Action: func(c *cli.Context) error {
+ return action.MirrorsList()
+ },
+ },
+ {
+ Name: "set",
+ Usage: "Set a mirror. This overwrites an existing entry if one exists",
+ Description: `Use 'set' in the form:
+
+ glide mirror set [original] [replacement]
+
+ or
+
+ glide mirror set [original] [replacement] --vcs [type]
+
+ for example,
+
+ glide mirror set https://github.com/example/foo https://git.example.com/example/foo.git
+
+ glide mirror set https://github.com/example/foo file:///path/to/local/repo --vcs git`,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "vcs",
+ Usage: "The VCS type to use. Autodiscovery is attempted when not supplied. Can be one of git, svn, bzr, or hg",
+ },
+ },
+ Action: func(c *cli.Context) error {
+ return action.MirrorsSet(c.Args().Get(0), c.Args().Get(1), c.String("vcs"))
+ },
+ },
+ {
+ Name: "remove",
+ ShortName: "rm",
+ Usage: "Remove an mirror",
+ Description: `Use 'remove' in the form:
+
+ glide mirror remove [original]
+
+ for example,
+
+ glide mirror remove https://github.com/example/foo`,
+ Action: func(c *cli.Context) error {
+ return action.MirrorsRemove(c.Args().Get(0))
+ },
+ },
},
},
}
@@ -711,6 +838,7 @@
action.Quiet(c.Bool("quiet"))
action.Init(c.String("yaml"), c.String("home"))
action.EnsureGoVendor()
+ gpath.Tmp = c.String("tmp")
return nil
}
diff --git a/glide.lock b/glide.lock
index c869e2f..2d3fc9c 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,12 +1,12 @@
-hash: 0653c17bcbf6f1df79990f3d2211dbcbc920ca528c513b00f5cab0a508c984ab
-updated: 2016-06-30T10:51:49.633776379-04:00
+hash: 67c5571c33bfcb663d32d2b40b9ce1f2a05a3fa2e9f442077277c2782195729c
+updated: 2016-08-11T14:22:17.773372627-04:00
imports:
- name: github.com/codegangsta/cli
- version: 71f57d300dd6a780ac1856c005c4b518cfd498ec
+ version: 1efa31f08b9333f1bd4882d61f9d668a70cd902e
- name: github.com/Masterminds/semver
version: 8d0431362b544d1a3536cca26684828866a7de09
- name: github.com/Masterminds/vcs
version: fbe9fb6ad5b5f35b3e82a7c21123cfc526cbf895
- name: gopkg.in/yaml.v2
- version: a83829b6f1293c91addabc89d0571c246397bbf4
+ version: e4d366fc3c7938e2958e662b4258c7a89e1f0e3e
testImports: []
diff --git a/glide.yaml b/glide.yaml
index dfc97e9..f293f35 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -13,6 +13,6 @@
- package: github.com/Masterminds/vcs
version: ^1.8.0
- package: github.com/codegangsta/cli
- version: ~1.14.0
+ version: ^1.16.0
- package: github.com/Masterminds/semver
version: ^1.1.1
diff --git a/mirrors/cfg.go b/mirrors/cfg.go
new file mode 100644
index 0000000..f749c9d
--- /dev/null
+++ b/mirrors/cfg.go
@@ -0,0 +1,98 @@
+package mirrors
+
+import (
+ "io/ioutil"
+ "sort"
+ "strings"
+
+ "gopkg.in/yaml.v2"
+)
+
+// Mirrors contains global mirrors to local configuration
+type Mirrors struct {
+
+ // Repos contains repo mirror configuration
+ Repos MirrorRepos `yaml:"repos"`
+}
+
+// Marshal converts a Mirror instance to YAML
+func (ov *Mirrors) Marshal() ([]byte, error) {
+ yml, err := yaml.Marshal(&ov)
+ if err != nil {
+ return []byte{}, err
+ }
+ return yml, nil
+}
+
+// WriteFile writes an mirrors.yaml file
+//
+// This is a convenience function that marshals the YAML and then writes it to
+// the given file. If the file exists, it will be clobbered.
+func (ov *Mirrors) WriteFile(opath string) error {
+ o, err := ov.Marshal()
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(opath, o, 0666)
+}
+
+// ReadMirrorsFile loads the contents of an mirrors.yaml file.
+func ReadMirrorsFile(opath string) (*Mirrors, error) {
+ yml, err := ioutil.ReadFile(opath)
+ if err != nil {
+ return nil, err
+ }
+ ov, err := FromYaml(yml)
+ if err != nil {
+ return nil, err
+ }
+ return ov, nil
+}
+
+// FromYaml returns an instance of Mirrors from YAML
+func FromYaml(yml []byte) (*Mirrors, error) {
+ ov := &Mirrors{}
+ err := yaml.Unmarshal([]byte(yml), &ov)
+ return ov, err
+}
+
+// MarshalYAML is a hook for gopkg.in/yaml.v2.
+// It sorts mirror repos lexicographically for reproducibility.
+func (ov *Mirrors) MarshalYAML() (interface{}, error) {
+
+ sort.Sort(ov.Repos)
+
+ return ov, nil
+}
+
+// MirrorRepos is a slice of Mirror pointers
+type MirrorRepos []*MirrorRepo
+
+// Len returns the length of the MirrorRepos. This is needed for sorting with
+// the sort package.
+func (o MirrorRepos) Len() int {
+ return len(o)
+}
+
+// Less is needed for the sort interface. It compares two MirrorRepos based on
+// their original value.
+func (o MirrorRepos) Less(i, j int) bool {
+
+ // Names are normalized to lowercase because case affects sorting order. For
+ // example, Masterminds comes before kylelemons. Making them lowercase
+ // causes kylelemons to come first which is what is expected.
+ return strings.ToLower(o[i].Original) < strings.ToLower(o[j].Original)
+}
+
+// Swap is needed for the sort interface. It swaps the position of two
+// MirrorRepos.
+func (o MirrorRepos) Swap(i, j int) {
+ o[i], o[j] = o[j], o[i]
+}
+
+// MirrorRepo represents a single repo mirror
+type MirrorRepo struct {
+ Original string `yaml:"original"`
+ Repo string `yaml:"repo"`
+ Vcs string `yaml:"vcs,omitempty"`
+}
diff --git a/mirrors/mirrors.go b/mirrors/mirrors.go
new file mode 100644
index 0000000..d42f50e
--- /dev/null
+++ b/mirrors/mirrors.go
@@ -0,0 +1,73 @@
+// Package mirrors handles managing mirrors in the running application
+package mirrors
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/Masterminds/glide/msg"
+ gpath "github.com/Masterminds/glide/path"
+)
+
+var mirrors map[string]*mirror
+
+func init() {
+ mirrors = make(map[string]*mirror)
+}
+
+type mirror struct {
+ Repo, Vcs string
+}
+
+// Get retrieves information about an mirror. It returns.
+// - bool if found
+// - new repo location
+// - vcs type
+func Get(k string) (bool, string, string) {
+ o, f := mirrors[k]
+ if !f {
+ return false, "", ""
+ }
+
+ return true, o.Repo, o.Vcs
+}
+
+// Load pulls the mirrors into memory
+func Load() error {
+ home := gpath.Home()
+
+ op := filepath.Join(home, "mirrors.yaml")
+
+ var ov *Mirrors
+ if _, err := os.Stat(op); os.IsNotExist(err) {
+ msg.Debug("No mirrors.yaml file exists")
+ ov = &Mirrors{
+ Repos: make(MirrorRepos, 0),
+ }
+ return nil
+ } else if err != nil {
+ ov = &Mirrors{
+ Repos: make(MirrorRepos, 0),
+ }
+ return err
+ }
+
+ var err error
+ ov, err = ReadMirrorsFile(op)
+ if err != nil {
+ return fmt.Errorf("Error reading existing mirrors.yaml file: %s", err)
+ }
+
+ msg.Info("Loading mirrors from mirrors.yaml file")
+ for _, o := range ov.Repos {
+ msg.Debug("Found mirror: %s to %s (%s)", o.Original, o.Repo, o.Vcs)
+ no := &mirror{
+ Repo: o.Repo,
+ Vcs: o.Vcs,
+ }
+ mirrors[o.Original] = no
+ }
+
+ return nil
+}
diff --git a/mirrors/mirrors_test.go b/mirrors/mirrors_test.go
new file mode 100644
index 0000000..bb01bb2
--- /dev/null
+++ b/mirrors/mirrors_test.go
@@ -0,0 +1,36 @@
+package mirrors
+
+import "testing"
+
+var oyml = `
+repos:
+- original: github.com/Masterminds/semver
+ repo: file:///path/to/local/repo
+ vcs: git
+- original: github.com/Masterminds/atest
+ repo: github.com/example/atest
+`
+
+var ooutyml = `repos:
+- original: github.com/Masterminds/atest
+ repo: github.com/example/atest
+- original: github.com/Masterminds/semver
+ repo: file:///path/to/local/repo
+ vcs: git
+`
+
+func TestSortMirrors(t *testing.T) {
+ ov, err := FromYaml([]byte(oyml))
+ if err != nil {
+ t.Error("Unable to read mirrors yaml")
+ }
+
+ out, err := ov.Marshal()
+ if err != nil {
+ t.Error("Unable to marshal mirrors yaml")
+ }
+
+ if string(out) != ooutyml {
+ t.Error("Output mirrors sorting failed")
+ }
+}
diff --git a/msg/msg.go b/msg/msg.go
index 34213fd..8b0f077 100644
--- a/msg/msg.go
+++ b/msg/msg.go
@@ -7,6 +7,8 @@
"os"
"strings"
"sync"
+
+ "github.com/Masterminds/vcs"
)
// Messenger provides the underlying implementation that displays output to
@@ -176,7 +178,6 @@
// locked to avoid displaying one message in the middle of another one.
m.Lock()
defer m.Unlock()
-
// Get rid of the annoying fact that messages need \n at the end, but do
// it in a backward compatible way.
if !strings.HasSuffix(msg, "\n") {
@@ -188,6 +189,21 @@
} else {
fmt.Fprintf(m.Stderr, msg, args...)
}
+
+ // If an arg is a vcs error print the output if in debug mode. This is
+ // capured here rather than calling Debug because concurrent operations
+ // could cause other messages to appear between the initial error and the
+ // debug output by unlocking and calling Debug.
+ if len(args) != 0 && !m.Quiet && m.IsDebugging {
+ if err, ok := args[len(args)-1].(error); ok {
+ switch t := err.(type) {
+ case *vcs.LocalError:
+ fmt.Fprintf(m.Stderr, "[DEBUG]\tOutput was: %s", strings.TrimSpace(t.Out()))
+ case *vcs.RemoteError:
+ fmt.Fprintf(m.Stderr, "[DEBUG]\tOutput was: %s", strings.TrimSpace(t.Out()))
+ }
+ }
+ }
}
// Msg prints a message with optional arguments, that can be printed, of
diff --git a/path/path.go b/path/path.go
index 0b19442..48e95fb 100644
--- a/path/path.go
+++ b/path/path.go
@@ -21,6 +21,10 @@
// As of Go 1.5, this is always vendor.
var VendorDir = "vendor"
+// Tmp is the temporary directory Glide should use. Defaults to "" which
+// signals using the system default.
+var Tmp = ""
+
// Cache the location of the homedirectory.
var homeDir = ""
@@ -80,6 +84,29 @@
gopath := filepath.Join(yamldir, VendorDir)
+ // Resolve symlinks
+ info, err := os.Lstat(gopath)
+ if err != nil {
+ return gopath, nil
+ }
+ for i := 0; IsLink(info) && i < 255; i++ {
+ p, err := os.Readlink(gopath)
+ if err != nil {
+ return gopath, nil
+ }
+
+ if filepath.IsAbs(p) {
+ gopath = p
+ } else {
+ gopath = filepath.Join(filepath.Dir(gopath), p)
+ }
+
+ info, err = os.Lstat(gopath)
+ if err != nil {
+ return gopath, nil
+ }
+ }
+
return gopath, nil
}
diff --git a/path/path_test.go b/path/path_test.go
index 0c8a221..77a0276 100644
--- a/path/path_test.go
+++ b/path/path_test.go
@@ -34,6 +34,7 @@
t.Fatal(err)
}
wd, _ := os.Getwd()
+
os.Chdir(filepath.Join(td, "a/b/c"))
res, err := Vendor()
if err != nil {
@@ -43,6 +44,17 @@
if res != expect {
t.Errorf("Failed to find vendor: expected %s got %s", expect, res)
}
+
+ os.Chdir(filepath.Join(td, "x/y/z"))
+ res, err = Vendor()
+ if err != nil {
+ t.Errorf("Failed to resolve vendor directory: %s", err)
+ }
+ expect = filepath.Join(td, "x", "symlinked_vendor")
+ if res != expect {
+ t.Errorf("Failed to find vendor: expected %s got %s", expect, res)
+ }
+
os.Chdir(wd)
}
func TestGlide(t *testing.T) {
diff --git a/path/strip.go b/path/strip.go
index 1162f38..62c5fd8 100644
--- a/path/strip.go
+++ b/path/strip.go
@@ -8,39 +8,10 @@
"github.com/Masterminds/glide/msg"
)
-// StripVcs removes VCS metadata (.git, .hg, .bzr, .svn) from the vendor/
-// directory.
-func StripVcs() error {
- if _, err := os.Stat(VendorDir); err != nil {
- if os.IsNotExist(err) {
- msg.Debug("Vendor directory does not exist.")
- }
-
- return err
- }
- return filepath.Walk(VendorDir, stripHandler)
-}
-
-func stripHandler(path string, info os.FileInfo, err error) error {
-
- name := info.Name()
- if name == ".git" || name == ".bzr" || name == ".svn" || name == ".hg" {
- if _, err := os.Stat(path); err == nil {
- if info.IsDir() {
- msg.Info("Removing: %s", path)
- return os.RemoveAll(path)
- }
-
- msg.Debug("%s is not a directory. Skipping removal", path)
- return nil
- }
- }
- return nil
-}
-
// StripVendor removes nested vendor and Godeps/_workspace/ directories.
func StripVendor() error {
- if _, err := os.Stat(VendorDir); err != nil {
+ searchPath, _ := Vendor()
+ if _, err := os.Stat(searchPath); err != nil {
if os.IsNotExist(err) {
msg.Debug("Vendor directory does not exist.")
}
@@ -48,33 +19,29 @@
return err
}
- err := filepath.Walk(VendorDir, stripVendorHandler)
+ err := filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
+ // Skip the base vendor directory
+ if path == searchPath {
+ return nil
+ }
+
+ name := info.Name()
+ if name == "vendor" {
+ if _, err := os.Stat(path); err == nil {
+ if info.IsDir() {
+ msg.Info("Removing: %s", path)
+ return os.RemoveAll(path)
+ }
+
+ msg.Debug("%s is not a directory. Skipping removal", path)
+ return nil
+ }
+ }
+ return nil
+ })
if err != nil {
return err
}
- err = strip.GodepWorkspace(VendorDir)
-
- return err
-}
-
-func stripVendorHandler(path string, info os.FileInfo, err error) error {
- // Skip the base vendor directory
- if path == VendorDir {
- return nil
- }
-
- name := info.Name()
- if name == "vendor" {
- if _, err := os.Stat(path); err == nil {
- if info.IsDir() {
- msg.Info("Removing: %s", path)
- return os.RemoveAll(path)
- }
-
- msg.Debug("%s is not a directory. Skipping removal", path)
- return nil
- }
- }
- return nil
+ return strip.GodepWorkspace(searchPath)
}
diff --git a/path/strip_test.go b/path/strip_test.go
deleted file mode 100644
index 6c11194..0000000
--- a/path/strip_test.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package path
-
-import (
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-)
-
-func TestStripVcs(t *testing.T) {
- tempDir, err := ioutil.TempDir("", "strip-vcs")
- if err != nil {
- t.Error(err)
- }
-
- defer func() {
- err = os.RemoveAll(tempDir)
- if err != nil {
- t.Error(err)
- }
- }()
-
- // Make VCS directories.
- gp := filepath.Join(tempDir, ".git")
- err = os.Mkdir(gp, 0755)
- if err != nil {
- t.Error(err)
- }
-
- bp := filepath.Join(tempDir, ".bzr")
- err = os.Mkdir(bp, 0755)
- if err != nil {
- t.Error(err)
- }
-
- hp := filepath.Join(tempDir, ".hg")
- err = os.Mkdir(hp, 0755)
- if err != nil {
- t.Error(err)
- }
-
- sp := filepath.Join(tempDir, ".svn")
- err = os.Mkdir(sp, 0755)
- if err != nil {
- t.Error(err)
- }
-
- ov := VendorDir
- VendorDir = tempDir
-
- StripVcs()
-
- VendorDir = ov
-
- if _, err := os.Stat(gp); !os.IsNotExist(err) {
- t.Error(".git directory not deleted")
- }
- if _, err := os.Stat(hp); !os.IsNotExist(err) {
- t.Error(".hg directory not deleted")
- }
- if _, err := os.Stat(bp); !os.IsNotExist(err) {
- t.Error(".bzr directory not deleted")
- }
- if _, err := os.Stat(sp); !os.IsNotExist(err) {
- t.Error(".svn directory not deleted")
- }
-}
diff --git a/repo/installer.go b/repo/installer.go
index 13d3fb0..324e4eb 100644
--- a/repo/installer.go
+++ b/repo/installer.go
@@ -2,6 +2,7 @@
import (
"fmt"
+ "io/ioutil"
"os"
"path/filepath"
"strings"
@@ -32,20 +33,6 @@
// Vendor contains the path to put the vendor packages
Vendor string
- // Use a cache
- UseCache bool
- // Use Gopath to cache
- UseCacheGopath bool
- // Use Gopath as a source to read from
- UseGopath bool
-
- // UpdateVendored instructs the environment to update in a way that is friendly
- // to packages that have been "vendored in" (e.g. are copies of source, not repos)
- UpdateVendored bool
-
- // DeleteUnused deletes packages that are unused, but found in the vendor dir.
- DeleteUnused bool
-
// ResolveAllFiles enables a resolver that will examine the dependencies
// of every file of every package, rather than only following imported
// packages.
@@ -82,11 +69,6 @@
// Install installs the dependencies from a Lockfile.
func (i *Installer) Install(lock *cfg.Lockfile, conf *cfg.Config) (*cfg.Config, error) {
- cwd, err := gpath.Vendor()
- if err != nil {
- return conf, err
- }
-
// Create a config setup based on the Lockfile data to process with
// existing commands.
newConf := &cfg.Config{}
@@ -104,16 +86,20 @@
newConf.DeDupe()
- if len(newConf.Imports) == 0 {
- msg.Info("No dependencies found. Nothing installed.\n")
+ if len(newConf.Imports) == 0 && len(newConf.DevImports) == 0 {
+ msg.Info("No dependencies found. Nothing installed.")
return newConf, nil
}
msg.Info("Downloading dependencies. Please wait...")
- LazyConcurrentUpdate(newConf.Imports, cwd, i, newConf)
- LazyConcurrentUpdate(newConf.DevImports, cwd, i, newConf)
- return newConf, nil
+ err := LazyConcurrentUpdate(newConf.Imports, i, newConf)
+ if err != nil {
+ return newConf, err
+ }
+ err = LazyConcurrentUpdate(newConf.DevImports, i, newConf)
+
+ return newConf, err
}
// Checkout reads the config file and checks out all dependencies mentioned there.
@@ -122,16 +108,14 @@
// vendor directory based on changed config.
func (i *Installer) Checkout(conf *cfg.Config) error {
- dest := i.VendorPath()
-
msg.Info("Downloading dependencies. Please wait...")
- if err := ConcurrentUpdate(conf.Imports, dest, i, conf); err != nil {
+ if err := ConcurrentUpdate(conf.Imports, i, conf); err != nil {
return err
}
if i.ResolveTest {
- return ConcurrentUpdate(conf.DevImports, dest, i, conf)
+ return ConcurrentUpdate(conf.DevImports, i, conf)
}
return nil
@@ -146,30 +130,22 @@
// In other words, all versions in the Lockfile will be empty.
func (i *Installer) Update(conf *cfg.Config) error {
base := "."
- vpath := i.VendorPath()
ic := newImportCache()
m := &MissingPackageHandler{
- destination: vpath,
-
- cache: i.UseCache,
- cacheGopath: i.UseCacheGopath,
- useGopath: i.UseGopath,
- home: i.Home,
- force: i.Force,
- updateVendored: i.UpdateVendored,
- Config: conf,
- Use: ic,
- updated: i.Updated,
+ home: i.Home,
+ force: i.Force,
+ Config: conf,
+ Use: ic,
+ updated: i.Updated,
}
v := &VersionHandler{
- Destination: vpath,
- Use: ic,
- Imported: make(map[string]bool),
- Conflicts: make(map[string]bool),
- Config: conf,
+ Use: ic,
+ Imported: make(map[string]bool),
+ Conflicts: make(map[string]bool),
+ Config: conf,
}
// Update imports
@@ -251,13 +227,13 @@
msg.Info("Downloading dependencies. Please wait...")
- err = ConcurrentUpdate(conf.Imports, vpath, i, conf)
+ err = ConcurrentUpdate(conf.Imports, i, conf)
if err != nil {
return err
}
if i.ResolveTest {
- err = ConcurrentUpdate(conf.DevImports, vpath, i, conf)
+ err = ConcurrentUpdate(conf.DevImports, i, conf)
if err != nil {
return err
}
@@ -266,19 +242,146 @@
return nil
}
+// Export from the cache to the vendor directory
+func (i *Installer) Export(conf *cfg.Config) error {
+ tempDir, err := ioutil.TempDir(gpath.Tmp, "glide-vendor")
+ if err != nil {
+ return err
+ }
+ defer func() {
+ err = os.RemoveAll(tempDir)
+ if err != nil {
+ msg.Err(err.Error())
+ }
+ }()
+
+ vp := filepath.Join(tempDir, "vendor")
+ err = os.MkdirAll(vp, 0755)
+
+ msg.Info("Exporting resolved dependencies...")
+ done := make(chan struct{}, concurrentWorkers)
+ in := make(chan *cfg.Dependency, concurrentWorkers)
+ var wg sync.WaitGroup
+ var lock sync.Mutex
+ var returnErr error
+
+ for ii := 0; ii < concurrentWorkers; ii++ {
+ go func(ch <-chan *cfg.Dependency) {
+ for {
+ select {
+ case dep := <-ch:
+ loc := dep.Remote()
+ key, err := cache.Key(loc)
+ if err != nil {
+ msg.Die(err.Error())
+ }
+ cache.Lock(key)
+
+ cdir := filepath.Join(cache.Location(), "src", key)
+ repo, err := dep.GetRepo(cdir)
+ if err != nil {
+ msg.Die(err.Error())
+ }
+ msg.Info("--> Exporting %s", dep.Name)
+ if err := repo.ExportDir(filepath.Join(vp, filepath.ToSlash(dep.Name))); err != nil {
+ msg.Err("Export failed for %s: %s\n", dep.Name, err)
+ // Capture the error while making sure the concurrent
+ // operations don't step on each other.
+ lock.Lock()
+ if returnErr == nil {
+ returnErr = err
+ } else {
+ returnErr = cli.NewMultiError(returnErr, err)
+ }
+ lock.Unlock()
+ }
+ cache.Unlock(key)
+ wg.Done()
+ case <-done:
+ return
+ }
+ }
+ }(in)
+ }
+
+ for _, dep := range conf.Imports {
+ if !conf.HasIgnore(dep.Name) {
+ err = os.MkdirAll(filepath.Join(vp, filepath.ToSlash(dep.Name)), 0755)
+ if err != nil {
+ lock.Lock()
+ if returnErr == nil {
+ returnErr = err
+ } else {
+ returnErr = cli.NewMultiError(returnErr, err)
+ }
+ lock.Unlock()
+ }
+ wg.Add(1)
+ in <- dep
+ }
+ }
+
+ if i.ResolveTest {
+ for _, dep := range conf.DevImports {
+ if !conf.HasIgnore(dep.Name) {
+ err = os.MkdirAll(filepath.Join(vp, filepath.ToSlash(dep.Name)), 0755)
+ if err != nil {
+ lock.Lock()
+ if returnErr == nil {
+ returnErr = err
+ } else {
+ returnErr = cli.NewMultiError(returnErr, err)
+ }
+ lock.Unlock()
+ }
+ wg.Add(1)
+ in <- dep
+ }
+ }
+ }
+
+ wg.Wait()
+
+ // Close goroutines setting the version
+ for ii := 0; ii < concurrentWorkers; ii++ {
+ done <- struct{}{}
+ }
+
+ if returnErr != nil {
+ return returnErr
+ }
+
+ msg.Info("Replacing existing vendor dependencies")
+ err = os.RemoveAll(i.VendorPath())
+ if err != nil {
+ return err
+ }
+
+ err = os.Rename(vp, i.VendorPath())
+
+ // When there are different physical devices we cannot rename cross device.
+ // Fall back to manual copy.
+ if err != nil && strings.Contains(err.Error(), "cross-device link") {
+ msg.Debug("Cross link err, trying manual copy: %s", err)
+
+ err = gpath.CopyDir(vp, i.VendorPath())
+ }
+
+ return err
+
+}
+
// List resolves the complete dependency tree and returns a list of dependencies.
func (i *Installer) List(conf *cfg.Config) []*cfg.Dependency {
base := "."
- vpath := i.VendorPath()
ic := newImportCache()
v := &VersionHandler{
- Destination: vpath,
- Use: ic,
- Imported: make(map[string]bool),
- Conflicts: make(map[string]bool),
- Config: conf,
+ Use: ic,
+ Imported: make(map[string]bool),
+ Conflicts: make(map[string]bool),
+ Config: conf,
}
// Update imports
@@ -311,11 +414,17 @@
// LazyConcurrentUpdate updates only deps that are not already checkout out at the right version.
//
// This is only safe when updating from a lock file.
-func LazyConcurrentUpdate(deps []*cfg.Dependency, cwd string, i *Installer, c *cfg.Config) error {
+func LazyConcurrentUpdate(deps []*cfg.Dependency, i *Installer, c *cfg.Config) error {
newDeps := []*cfg.Dependency{}
for _, dep := range deps {
- destPath := filepath.Join(i.VendorPath(), dep.Name)
+
+ key, err := cache.Key(dep.Remote())
+ if err != nil {
+ newDeps = append(newDeps, dep)
+ continue
+ }
+ destPath := filepath.Join(cache.Location(), "src", key)
// Get a VCS object for this directory
repo, err := dep.GetRepo(destPath)
@@ -329,24 +438,26 @@
newDeps = append(newDeps, dep)
continue
}
-
- if ver == dep.Reference {
- msg.Info("--> Found desired version %s %s!", dep.Name, dep.Reference)
- continue
+ if dep.Reference != "" {
+ ci, err := repo.CommitInfo(dep.Reference)
+ if err == nil && ci.Commit == dep.Reference {
+ msg.Info("--> Found desired version locally %s %s!", dep.Name, dep.Reference)
+ continue
+ }
}
msg.Debug("--> Queue %s for update (%s != %s).", dep.Name, ver, dep.Reference)
newDeps = append(newDeps, dep)
}
if len(newDeps) > 0 {
- return ConcurrentUpdate(newDeps, cwd, i, c)
+ return ConcurrentUpdate(newDeps, i, c)
}
return nil
}
// ConcurrentUpdate takes a list of dependencies and updates in parallel.
-func ConcurrentUpdate(deps []*cfg.Dependency, cwd string, i *Installer, c *cfg.Config) error {
+func ConcurrentUpdate(deps []*cfg.Dependency, i *Installer, c *cfg.Config) error {
done := make(chan struct{}, concurrentWorkers)
in := make(chan *cfg.Dependency, concurrentWorkers)
var wg sync.WaitGroup
@@ -358,19 +469,13 @@
for {
select {
case dep := <-ch:
- var loc string
- if dep.Repository != "" {
- loc = dep.Repository
- } else {
- loc = "https://" + dep.Name
- }
+ loc := dep.Remote()
key, err := cache.Key(loc)
if err != nil {
msg.Die(err.Error())
}
cache.Lock(key)
- dest := filepath.Join(i.VendorPath(), dep.Name)
- if err := VcsUpdate(dep, dest, i.Home, i.UseCache, i.UseCacheGopath, i.UseGopath, i.Force, i.UpdateVendored, i.Updated); err != nil {
+ if err := VcsUpdate(dep, i.Force, i.Updated); err != nil {
msg.Err("Update failed for %s: %s\n", dep.Name, err)
// Capture the error while making sure the concurrent
// operations don't step on each other.
@@ -436,122 +541,76 @@
//
// When a package is found on the GOPATH, this notifies the user.
type MissingPackageHandler struct {
- destination string
- home string
- cache, cacheGopath, useGopath, force, updateVendored bool
- Config *cfg.Config
- Use *importCache
- updated *UpdateTracker
+ home string
+ force bool
+ Config *cfg.Config
+ Use *importCache
+ updated *UpdateTracker
}
-// NotFound attempts to retrieve a package when not found in the local vendor/
+// NotFound attempts to retrieve a package when not found in the local cache
// folder. It will attempt to get it from the remote location info.
func (m *MissingPackageHandler) NotFound(pkg string, addTest bool) (bool, error) {
- root := util.GetRootFromPackage(pkg)
- // Skip any references to the root package.
- if root == m.Config.Name {
- return false, nil
- }
-
- dest := filepath.Join(m.destination, root)
-
- // This package may have been placed on the list to look for when it wasn't
- // downloaded but it has since been downloaded before coming to this entry.
- if _, err := os.Stat(dest); err == nil {
- // Make sure the location contains files. It may be an empty directory.
- empty, err := gpath.IsDirectoryEmpty(dest)
- if err != nil {
- return false, err
- }
- if empty {
- msg.Warn("%s is an existing location with no files. Fetching a new copy of the dependency.", dest)
- msg.Debug("Removing empty directory %s", dest)
- err := os.RemoveAll(dest)
- if err != nil {
- msg.Debug("Installer error removing directory %s: %s", dest, err)
- return false, err
- }
- } else {
- msg.Debug("Found %s", dest)
- return true, nil
- }
- }
-
- msg.Info("Fetching %s into %s", pkg, m.destination)
-
- d := m.Config.Imports.Get(root)
- if d == nil && addTest {
- d = m.Config.DevImports.Get(root)
- }
-
- // If the dependency is nil it means the Config doesn't yet know about it.
- if d == nil {
- d, _ = m.Use.Get(root)
- // We don't know about this dependency so we create a basic instance.
- if d == nil {
- d = &cfg.Dependency{Name: root}
- }
- if addTest {
- m.Config.DevImports = append(m.Config.DevImports, d)
- } else {
- m.Config.Imports = append(m.Config.Imports, d)
- }
- }
- if err := VcsGet(d, dest, m.home, m.cache, m.cacheGopath, m.useGopath); err != nil {
+ err := m.fetchToCache(pkg, addTest)
+ if err != nil {
return false, err
}
- return true, nil
+
+ return true, err
}
// OnGopath will either copy a package, already found in the GOPATH, to the
// vendor/ directory or download it from the internet. This is dependent if
// useGopath on the installer is set to true to copy from the GOPATH.
func (m *MissingPackageHandler) OnGopath(pkg string, addTest bool) (bool, error) {
- // If useGopath is false, we fall back to the strategy of fetching from
- // remote.
- if !m.useGopath {
- return m.NotFound(pkg, addTest)
+
+ err := m.fetchToCache(pkg, addTest)
+ if err != nil {
+ return false, err
}
- root := util.GetRootFromPackage(pkg)
-
- // Skip any references to the root package.
- if root == m.Config.Name {
- return false, nil
- }
-
- msg.Info("Copying package %s from the GOPATH.", pkg)
- dest := filepath.Join(m.destination, pkg)
- // Find package on Gopath
- for _, gp := range gpath.Gopaths() {
- src := filepath.Join(gp, pkg)
- // FIXME: Should probably check if src is a dir or symlink.
- if _, err := os.Stat(src); err == nil {
- if err := os.MkdirAll(dest, os.ModeDir|0755); err != nil {
- return false, err
- }
- if err := gpath.CopyDir(src, dest); err != nil {
- return false, err
- }
- return true, nil
- }
- }
-
- msg.Err("Could not locate %s on the GOPATH, though it was found before.", pkg)
- return false, nil
+ return true, err
}
// InVendor updates a package in the vendor/ directory to make sure the latest
// is available.
func (m *MissingPackageHandler) InVendor(pkg string, addTest bool) error {
+ return m.fetchToCache(pkg, addTest)
+}
+
+// PkgPath resolves the location on the filesystem where the package should be.
+// This handles making sure to use the cache location.
+func (m *MissingPackageHandler) PkgPath(pkg string) string {
+ root, sub := util.NormalizeName(pkg)
+
+ d := m.Config.Imports.Get(root)
+ if d == nil {
+ d = m.Config.DevImports.Get(root)
+ }
+
+ if d == nil {
+ d, _ = m.Use.Get(root)
+
+ if d == nil {
+ d = &cfg.Dependency{Name: root}
+ }
+ }
+
+ key, err := cache.Key(d.Remote())
+ if err != nil {
+ msg.Die("Error generating cache key for %s", d.Name)
+ }
+
+ return filepath.Join(cache.Location(), "src", key, sub)
+}
+
+func (m *MissingPackageHandler) fetchToCache(pkg string, addTest bool) error {
root := util.GetRootFromPackage(pkg)
// Skip any references to the root package.
if root == m.Config.Name {
return nil
}
- dest := filepath.Join(m.destination, root)
-
d := m.Config.Imports.Get(root)
if d == nil && addTest {
d = m.Config.DevImports.Get(root)
@@ -572,11 +631,7 @@
}
}
- if err := VcsUpdate(d, dest, m.home, m.cache, m.cacheGopath, m.useGopath, m.force, m.updateVendored, m.updated); err != nil {
- return err
- }
-
- return nil
+ return VcsUpdate(d, m.force, m.updated)
}
// VersionHandler handles setting the proper version in the VCS.
@@ -589,9 +644,6 @@
// Cache if importing scan has already occurred here.
Imported map[string]bool
- // Where the packages exist to set the version on.
- Destination string
-
Config *cfg.Config
// There's a problem where many sub-packages have been asked to set a version
@@ -614,7 +666,7 @@
// Should we look in places other than the root of the project?
if d.Imported[root] == false {
d.Imported[root] = true
- p := filepath.Join(d.Destination, root)
+ p := d.pkgPath(pkg)
f, deps, err := importer.Import(p)
if f && err == nil {
for _, dep := range deps {
@@ -671,7 +723,7 @@
v.Pin = ""
dep = v
} else if v.Reference != "" && dep.Reference != "" && v.Reference != dep.Reference {
- dest := filepath.Join(d.Destination, filepath.FromSlash(v.Name))
+ dest := d.pkgPath(pkg)
dep = determineDependency(v, dep, dest, req)
} else {
dep = v
@@ -703,7 +755,7 @@
}
}
- err := VcsVersion(dep, d.Destination)
+ err := VcsVersion(dep)
if err != nil {
msg.Warn("Unable to set version on %s to %s. Err: %s", root, dep.Reference, err)
e = err
@@ -712,6 +764,30 @@
return
}
+func (d *VersionHandler) pkgPath(pkg string) string {
+ root, sub := util.NormalizeName(pkg)
+
+ dep := d.Config.Imports.Get(root)
+ if dep == nil {
+ dep = d.Config.DevImports.Get(root)
+ }
+
+ if dep == nil {
+ dep, _ = d.Use.Get(root)
+
+ if dep == nil {
+ dep = &cfg.Dependency{Name: root}
+ }
+ }
+
+ key, err := cache.Key(dep.Remote())
+ if err != nil {
+ msg.Die("Error generating cache key for %s", dep.Name)
+ }
+
+ return filepath.Join(cache.Location(), "src", key, sub)
+}
+
func determineDependency(v, dep *cfg.Dependency, dest, req string) *cfg.Dependency {
repo, err := v.GetRepo(dest)
if err != nil {
diff --git a/repo/set_reference.go b/repo/set_reference.go
index 4363459..d9899ec 100644
--- a/repo/set_reference.go
+++ b/repo/set_reference.go
@@ -3,20 +3,16 @@
import (
"sync"
+ "github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/msg"
- gpath "github.com/Masterminds/glide/path"
+ "github.com/codegangsta/cli"
)
// SetReference is a command to set the VCS reference (commit id, tag, etc) for
// a project.
func SetReference(conf *cfg.Config, resolveTest bool) error {
- cwd, err := gpath.Vendor()
- if err != nil {
- return err
- }
-
if len(conf.Imports) == 0 && len(conf.DevImports) == 0 {
msg.Info("No references set.\n")
return nil
@@ -25,15 +21,40 @@
done := make(chan struct{}, concurrentWorkers)
in := make(chan *cfg.Dependency, concurrentWorkers)
var wg sync.WaitGroup
+ var lock sync.Mutex
+ var returnErr error
for i := 0; i < concurrentWorkers; i++ {
go func(ch <-chan *cfg.Dependency) {
for {
select {
case dep := <-ch:
- if err := VcsVersion(dep, cwd); err != nil {
- msg.Err("Failed to set version on %s to %s: %s\n", dep.Name, dep.Reference, err)
+
+ var loc string
+ if dep.Repository != "" {
+ loc = dep.Repository
+ } else {
+ loc = "https://" + dep.Name
}
+ key, err := cache.Key(loc)
+ if err != nil {
+ msg.Die(err.Error())
+ }
+ cache.Lock(key)
+ if err := VcsVersion(dep); err != nil {
+ msg.Err("Failed to set version on %s to %s: %s\n", dep.Name, dep.Reference, err)
+
+ // Capture the error while making sure the concurrent
+ // operations don't step on each other.
+ lock.Lock()
+ if returnErr == nil {
+ returnErr = err
+ } else {
+ returnErr = cli.NewMultiError(returnErr, err)
+ }
+ lock.Unlock()
+ }
+ cache.Unlock(key)
wg.Done()
case <-done:
return
@@ -66,5 +87,5 @@
// close(done)
// close(in)
- return nil
+ return returnErr
}
diff --git a/repo/vcs.go b/repo/vcs.go
index 3f5f80d..57ae33c 100644
--- a/repo/vcs.go
+++ b/repo/vcs.go
@@ -21,7 +21,7 @@
)
// VcsUpdate updates to a particular checkout based on the VCS setting.
-func VcsUpdate(dep *cfg.Dependency, dest, home string, cache, cacheGopath, useGopath, force, updateVendored bool, updated *UpdateTracker) error {
+func VcsUpdate(dep *cfg.Dependency, force bool, updated *UpdateTracker) error {
// If the dependency has already been pinned we can skip it. This is a
// faster path so we don't need to resolve it again.
@@ -36,21 +36,28 @@
}
updated.Add(dep.Name)
- msg.Info("--> Fetching updates for %s.", dep.Name)
-
if filterArchOs(dep) {
msg.Info("%s is not used for %s/%s.\n", dep.Name, runtime.GOOS, runtime.GOARCH)
return nil
}
+ key, err := cp.Key(dep.Remote())
+ if err != nil {
+ msg.Die("Cache key generation error: %s", err)
+ }
+ location := cp.Location()
+ dest := filepath.Join(location, "src", key)
+
// If destination doesn't exist we need to perform an initial checkout.
if _, err := os.Stat(dest); os.IsNotExist(err) {
- if err = VcsGet(dep, dest, home, cache, cacheGopath, useGopath); err != nil {
+ msg.Info("--> Fetching %s.", dep.Name)
+ if err = VcsGet(dep); err != nil {
msg.Warn("Unable to checkout %s\n", dep.Name)
return err
}
} else {
// At this point we have a directory for the package.
+ msg.Info("--> Fetching updates for %s.", dep.Name)
// When the directory is not empty and has no VCS directory it's
// a vendored files situation.
@@ -59,40 +66,18 @@
return err
}
_, err = v.DetectVcsFromFS(dest)
- if updateVendored == false && empty == false && err == v.ErrCannotDetectVCS {
- msg.Warn("%s appears to be a vendored package. Unable to update. Consider the '--update-vendored' flag.\n", dep.Name)
- } else if updateVendored == false && empty == true && err == v.ErrCannotDetectVCS {
- msg.Warn("%s is an empty directory. Fetching a new copy of the dependency.", dep.Name)
+ if empty == true && err == v.ErrCannotDetectVCS {
+ msg.Warn("Cached version of %s is an empty directory. Fetching a new copy of the dependency.", dep.Name)
msg.Debug("Removing empty directory %s", dest)
err := os.RemoveAll(dest)
if err != nil {
return err
}
- if err = VcsGet(dep, dest, home, cache, cacheGopath, useGopath); err != nil {
+ if err = VcsGet(dep); err != nil {
msg.Warn("Unable to checkout %s\n", dep.Name)
return err
}
} else {
-
- if updateVendored == true && empty == false && err == v.ErrCannotDetectVCS {
- // A vendored package, no repo, and updating the vendored packages
- // has been opted into.
- msg.Info("%s is a vendored package. Updating.", dep.Name)
- err = os.RemoveAll(dest)
- if err != nil {
- msg.Err("Unable to update vendored dependency %s.\n", dep.Name)
- return err
- }
- dep.UpdateAsVendored = true
-
- if err = VcsGet(dep, dest, home, cache, cacheGopath, useGopath); err != nil {
- msg.Warn("Unable to checkout %s\n", dep.Name)
- return err
- }
-
- return nil
- }
-
repo, err := dep.GetRepo(dest)
// Tried to checkout a repo to a path that does not work. Either the
@@ -101,19 +86,14 @@
// Warning, any changes in the old location will be deleted.
// TODO: Put dirty checking in on the existing local checkout.
if (err == v.ErrWrongVCS || err == v.ErrWrongRemote) && force == true {
- var newRemote string
- if len(dep.Repository) > 0 {
- newRemote = dep.Repository
- } else {
- newRemote = "https://" + dep.Name
- }
+ newRemote := dep.Remote()
msg.Warn("Replacing %s with contents from %s\n", dep.Name, newRemote)
rerr := os.RemoveAll(dest)
if rerr != nil {
return rerr
}
- if err = VcsGet(dep, dest, home, cache, cacheGopath, useGopath); err != nil {
+ if err = VcsGet(dep); err != nil {
msg.Warn("Unable to checkout %s\n", dep.Name)
return err
}
@@ -128,16 +108,20 @@
return fmt.Errorf("%s contains uncommitted changes. Skipping update", dep.Name)
}
+ ver := dep.Reference
+ if ver == "" {
+ ver = defaultBranch(repo)
+ }
// Check if the current version is a tag or commit id. If it is
// and that version is already checked out we can skip updating
// which is faster than going out to the Internet to perform
// an update.
- if dep.Reference != "" {
+ if ver != "" {
version, err := repo.Version()
if err != nil {
return err
}
- ib, err := isBranch(dep.Reference, repo)
+ ib, err := isBranch(ver, repo)
if err != nil {
return err
}
@@ -145,7 +129,7 @@
// If the current version equals the ref and it's not a
// branch it's a tag or commit id so we can skip
// performing an update.
- if version == dep.Reference && !ib {
+ if version == ver && !ib {
msg.Debug("%s is already set to version %s. Skipping update.", dep.Name, dep.Reference)
return nil
}
@@ -162,7 +146,7 @@
}
// VcsVersion set the VCS version for a checkout.
-func VcsVersion(dep *cfg.Dependency, vend string) error {
+func VcsVersion(dep *cfg.Dependency) error {
// If the dependency has already been pinned we can skip it. This is a
// faster path so we don't need to resolve it again.
@@ -171,7 +155,12 @@
return nil
}
- cwd := filepath.Join(vend, dep.Name)
+ key, err := cp.Key(dep.Remote())
+ if err != nil {
+ msg.Die("Cache key generation error: %s", err)
+ }
+ location := cp.Location()
+ cwd := filepath.Join(location, "src", key)
// If there is no reference configured there is nothing to set.
if dep.Reference == "" {
@@ -195,65 +184,65 @@
}
_, err = v.DetectVcsFromFS(cwd)
if empty == false && err == v.ErrCannotDetectVCS {
- msg.Warn("%s appears to be a vendored package. Unable to set new version. Consider the '--update-vendored' flag.\n", dep.Name)
+ return fmt.Errorf("Cache directory missing VCS information for %s", dep.Name)
+ }
+
+ repo, err := dep.GetRepo(cwd)
+ if err != nil {
+ return err
+ }
+
+ ver := dep.Reference
+ // References in Git can begin with a ^ which is similar to semver.
+ // If there is a ^ prefix we assume it's a semver constraint rather than
+ // part of the git/VCS commit id.
+ if repo.IsReference(ver) && !strings.HasPrefix(ver, "^") {
+ msg.Info("--> Setting version for %s to %s.\n", dep.Name, ver)
} else {
- repo, err := dep.GetRepo(cwd)
+
+ // Create the constraint first to make sure it's valid before
+ // working on the repo.
+ constraint, err := semver.NewConstraint(ver)
+
+ // Make sure the constriant is valid. At this point it's not a valid
+ // reference so if it's not a valid constrint we can exit early.
+ if err != nil {
+ msg.Warn("The reference '%s' is not valid\n", ver)
+ return err
+ }
+
+ // Get the tags and branches (in that order)
+ refs, err := getAllVcsRefs(repo)
if err != nil {
return err
}
- ver := dep.Reference
- // References in Git can begin with a ^ which is similar to semver.
- // If there is a ^ prefix we assume it's a semver constraint rather than
- // part of the git/VCS commit id.
- if repo.IsReference(ver) && !strings.HasPrefix(ver, "^") {
- msg.Info("--> Setting version for %s to %s.\n", dep.Name, ver)
+ // Convert and filter the list to semver.Version instances
+ semvers := getSemVers(refs)
+
+ // Sort semver list
+ sort.Sort(sort.Reverse(semver.Collection(semvers)))
+ found := false
+ for _, v := range semvers {
+ if constraint.Check(v) {
+ found = true
+ // If the constrint passes get the original reference
+ ver = v.Original()
+ break
+ }
+ }
+ if found {
+ msg.Info("--> Detected semantic version. Setting version for %s to %s.", dep.Name, ver)
} else {
-
- // Create the constraint first to make sure it's valid before
- // working on the repo.
- constraint, err := semver.NewConstraint(ver)
-
- // Make sure the constriant is valid. At this point it's not a valid
- // reference so if it's not a valid constrint we can exit early.
- if err != nil {
- msg.Warn("The reference '%s' is not valid\n", ver)
- return err
- }
-
- // Get the tags and branches (in that order)
- refs, err := getAllVcsRefs(repo)
- if err != nil {
- return err
- }
-
- // Convert and filter the list to semver.Version instances
- semvers := getSemVers(refs)
-
- // Sort semver list
- sort.Sort(sort.Reverse(semver.Collection(semvers)))
- found := false
- for _, v := range semvers {
- if constraint.Check(v) {
- found = true
- // If the constrint passes get the original reference
- ver = v.Original()
- break
- }
- }
- if found {
- msg.Info("--> Detected semantic version. Setting version for %s to %s.", dep.Name, ver)
- } else {
- msg.Warn("--> Unable to find semantic version for constraint %s %s", dep.Name, ver)
- }
+ msg.Warn("--> Unable to find semantic version for constraint %s %s", dep.Name, ver)
}
- if err := repo.UpdateVersion(ver); err != nil {
- return err
- }
- dep.Pin, err = repo.Version()
- if err != nil {
- return err
- }
+ }
+ if err := repo.UpdateVersion(ver); err != nil {
+ return err
+ }
+ dep.Pin, err = repo.Version()
+ if err != nil {
+ return err
}
return nil
@@ -261,215 +250,47 @@
// VcsGet figures out how to fetch a dependency, and then gets it.
//
-// VcsGet installs into the dest.
-func VcsGet(dep *cfg.Dependency, dest, home string, cache, cacheGopath, useGopath bool) error {
- // When not skipping the $GOPATH look in it for a copy of the package
- if useGopath {
- // Check if the $GOPATH has a viable version to use and if so copy to vendor
- gps := gpath.Gopaths()
- for _, p := range gps {
- d := filepath.Join(p, "src", dep.Name)
- if _, err := os.Stat(d); err == nil {
- empty, err := gpath.IsDirectoryEmpty(d)
- if empty || err != nil {
- continue
- }
+// VcsGet installs into the cache.
+func VcsGet(dep *cfg.Dependency) error {
- repo, err := dep.GetRepo(d)
- if err != nil {
- continue
- }
-
- // Dirty repos have uncommitted changes.
- if repo.IsDirty() {
- continue
- }
-
- // Having found a repo we copy it to vendor and update it.
- msg.Info("Copying package %s from the GOPATH.", dep.Name)
- msg.Debug("Found %s in GOPATH at %s. Copying to %s", dep.Name, d, dest)
- err = gpath.CopyDir(d, dest)
- if err != nil {
- return err
- }
-
- // Update the repo in the vendor directory
- msg.Debug("Updating %s, now in the vendor path at %s", dep.Name, dest)
- repo, err = dep.GetRepo(dest)
- if err != nil {
- return err
- }
- err = repo.Update()
- if err != nil {
- return err
- }
-
- // If there is no reference set on the dep we try to checkout
- // the default branch.
- if dep.Reference == "" {
- db := defaultBranch(repo, home)
- if db != "" {
- err = repo.UpdateVersion(db)
- if err != nil && msg.Default.IsDebugging {
- msg.Debug("Attempting to set the version on %s to %s failed. Error %s", dep.Name, db, err)
- }
- }
- }
- return nil
- }
- }
+ key, err := cp.Key(dep.Remote())
+ if err != nil {
+ msg.Die("Cache key generation error: %s", err)
}
+ location := cp.Location()
+ d := filepath.Join(location, "src", key)
- // When opting in to cache in the GOPATH attempt to do put a copy there.
- if cacheGopath {
-
- // Since we didn't find an existing copy in the GOPATHs try to clone there.
- gp := gpath.Gopath()
- if gp != "" {
- d := filepath.Join(gp, "src", dep.Name)
- if _, err := os.Stat(d); os.IsNotExist(err) {
- // Empty directory so we checkout out the code here.
- msg.Debug("Retrieving %s to %s before copying to vendor", dep.Name, d)
- repo, err := dep.GetRepo(d)
- if err != nil {
- return err
- }
- repo.Get()
-
- branch := findCurrentBranch(repo)
- if branch != "" {
- // we know the default branch so we can store it in the cache
- var loc string
- if dep.Repository != "" {
- loc = dep.Repository
- } else {
- loc = "https://" + dep.Name
- }
- key, err := cp.Key(loc)
- if err == nil {
- msg.Debug("Saving default branch for %s", repo.Remote())
- c := cp.RepoInfo{DefaultBranch: branch}
- err = cp.SaveRepoData(key, c)
- if msg.Default.IsDebugging && err == cp.ErrCacheDisabled {
- msg.Debug("Unable to cache default branch because caching is disabled")
- }
- }
- }
-
- msg.Debug("Copying %s from GOPATH at %s to %s", dep.Name, d, dest)
- err = gpath.CopyDir(d, dest)
- if err != nil {
- return err
- }
-
- return nil
- }
- }
- }
-
- // If opting in to caching attempt to put it in the cache folder
- if cache {
- // Check if the cache has a viable version and try to use that.
- var loc string
- if dep.Repository != "" {
- loc = dep.Repository
- } else {
- loc = "https://" + dep.Name
- }
- key, err := cp.Key(loc)
- if err == nil {
- location, err := cp.Location()
- if err != nil {
- return err
- }
- d := filepath.Join(location, "src", key)
-
- repo, err := dep.GetRepo(d)
- if err != nil {
- return err
- }
- // If the directory does not exist this is a first cache.
- if _, err = os.Stat(d); os.IsNotExist(err) {
- msg.Debug("Adding %s to the cache for the first time", dep.Name)
- err = repo.Get()
- if err != nil {
- return err
- }
- branch := findCurrentBranch(repo)
- if branch != "" {
- // we know the default branch so we can store it in the cache
- var loc string
- if dep.Repository != "" {
- loc = dep.Repository
- } else {
- loc = "https://" + dep.Name
- }
- key, err := cp.Key(loc)
- if err == nil {
- msg.Debug("Saving default branch for %s", repo.Remote())
- c := cp.RepoInfo{DefaultBranch: branch}
- err = cp.SaveRepoData(key, c)
- if err == cp.ErrCacheDisabled {
- msg.Debug("Unable to cache default branch because caching is disabled")
- } else if err != nil {
- msg.Debug("Error saving %s to cache. Error: %s", repo.Remote(), err)
- }
- }
- }
-
- } else {
- msg.Debug("Updating %s in the cache", dep.Name)
- err = repo.Update()
- if err != nil {
- return err
- }
- }
-
- msg.Debug("Copying %s from the cache to %s", dep.Name, dest)
- err = gpath.CopyDir(d, dest)
- if err != nil {
- return err
- }
-
- return nil
- }
-
- msg.Warn("Cache key generation error: %s", err)
- }
-
- // If unable to cache pull directly into the vendor/ directory.
- repo, err := dep.GetRepo(dest)
+ repo, err := dep.GetRepo(d)
if err != nil {
return err
}
-
- gerr := repo.Get()
-
- // Attempt to cache the default branch
- if cache {
- if branch := findCurrentBranch(repo); branch != "" {
- // we know the default branch so we can store it in the cache
- var loc string
- if dep.Repository != "" {
- loc = dep.Repository
- } else {
- loc = "https://" + dep.Name
+ // If the directory does not exist this is a first cache.
+ if _, err = os.Stat(d); os.IsNotExist(err) {
+ msg.Debug("Adding %s to the cache for the first time", dep.Name)
+ err = repo.Get()
+ if err != nil {
+ return err
+ }
+ branch := findCurrentBranch(repo)
+ if branch != "" {
+ msg.Debug("Saving default branch for %s", repo.Remote())
+ c := cp.RepoInfo{DefaultBranch: branch}
+ err = cp.SaveRepoData(key, c)
+ if err == cp.ErrCacheDisabled {
+ msg.Debug("Unable to cache default branch because caching is disabled")
+ } else if err != nil {
+ msg.Debug("Error saving %s to cache. Error: %s", repo.Remote(), err)
}
- key, err := cp.Key(loc)
- if err == nil {
- msg.Debug("Saving default branch for %s", repo.Remote())
- c := cp.RepoInfo{DefaultBranch: branch}
- err = cp.SaveRepoData(key, c)
- if err == cp.ErrCacheDisabled {
- msg.Debug("Unable to cache default branch because caching is disabled")
- } else if err != nil {
- msg.Debug("Error saving %s to cache - Error: %s", repo.Remote(), err)
- }
- }
+ }
+ } else {
+ msg.Debug("Updating %s in the cache", dep.Name)
+ err = repo.Update()
+ if err != nil {
+ return err
}
}
- return gerr
+ return nil
}
// filterArchOs indicates a dependency should be filtered out because it is
@@ -523,7 +344,7 @@
// defaultBranch tries to ascertain the default branch for the given repo.
// Some repos will have multiple branches in them (e.g. Git) while others
// (e.g. Svn) will not.
-func defaultBranch(repo v.Repo, home string) string {
+func defaultBranch(repo v.Repo) string {
// Svn and Bzr use different locations (paths or entire locations)
// for branches so we won't have a default branch.
diff --git a/repo/vendored_cleanup.go b/repo/vendored_cleanup.go
deleted file mode 100644
index acbab91..0000000
--- a/repo/vendored_cleanup.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package repo
-
-import (
- "os"
- "path/filepath"
-
- "github.com/Masterminds/glide/cfg"
- "github.com/Masterminds/glide/msg"
- gpath "github.com/Masterminds/glide/path"
-)
-
-// VendoredCleanup cleans up vendored codebases after an update.
-//
-// This should _only_ be run for installations that do not want VCS repos inside
-// of the vendor/ directory.
-func VendoredCleanup(conf *cfg.Config) error {
- vend, err := gpath.Vendor()
- if err != nil {
- return err
- }
-
- for _, dep := range conf.Imports {
- if dep.UpdateAsVendored == true {
- msg.Info("Cleaning up vendored package %s\n", dep.Name)
-
- // Remove the VCS directory
- cwd := filepath.Join(vend, dep.Name)
- repo, err := dep.GetRepo(cwd)
- if err != nil {
- msg.Err("Error cleaning up %s:%s", dep.Name, err)
- continue
- }
- t := repo.Vcs()
- err = os.RemoveAll(cwd + string(os.PathSeparator) + "." + string(t))
- if err != nil {
- msg.Err("Error cleaning up VCS dir for %s:%s", dep.Name, err)
- }
- }
-
- }
-
- return nil
-}
diff --git a/testdata/path/x/glide.yaml b/testdata/path/x/glide.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testdata/path/x/glide.yaml
diff --git a/testdata/path/x/vendor b/testdata/path/x/vendor
new file mode 120000
index 0000000..5b13353
--- /dev/null
+++ b/testdata/path/x/vendor
@@ -0,0 +1 @@
+symlinked_vendor
\ No newline at end of file
diff --git a/testdata/path/x/y/z/placeholder.empty b/testdata/path/x/y/z/placeholder.empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testdata/path/x/y/z/placeholder.empty
diff --git a/tree/tree.go b/tree/tree.go
index 0dc3df7..e16478c 100644
--- a/tree/tree.go
+++ b/tree/tree.go
@@ -45,6 +45,10 @@
func walkDeps(b *util.BuildCtxt, base, myName string) []string {
externalDeps := []string{}
filepath.Walk(base, func(path string, fi os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
if !dependency.IsSrcDir(fi) {
if fi.IsDir() {
return filepath.SkipDir
diff --git a/util/normalizename_test.go b/util/normalizename_test.go
index c6f4d02..d5a454b 100644
--- a/util/normalizename_test.go
+++ b/util/normalizename_test.go
@@ -31,11 +31,18 @@
extra: "",
},
{
+ input: "otherurl/example/root/sub",
+ root: "otherurl/example/root",
+ extra: "sub",
+ },
+ {
input: "net",
root: "net",
extra: "",
},
}
+ remotePackageCache["otherurl/example/root"] = "otherurl/example/root"
+
for _, test := range packages {
root, extra := NormalizeName(test.input)
if root != test.root {
diff --git a/util/util.go b/util/util.go
index a5b346f..1226b0b 100644
--- a/util/util.go
+++ b/util/util.go
@@ -110,7 +110,7 @@
func checkRemotePackageCache(pkg string) (string, bool) {
for k, v := range remotePackageCache {
- if pkg == k {
+ if pkg == k || strings.HasPrefix(pkg, k+"/") {
return v, true
}
}
diff --git a/vendor/github.com/codegangsta/cli/.gitignore b/vendor/github.com/codegangsta/cli/.gitignore
new file mode 100644
index 0000000..faf70c4
--- /dev/null
+++ b/vendor/github.com/codegangsta/cli/.gitignore
@@ -0,0 +1,2 @@
+*.coverprofile
+node_modules/
diff --git a/vendor/github.com/codegangsta/cli/.travis.yml b/vendor/github.com/codegangsta/cli/.travis.yml
index c2b5c8d..273d017 100644
--- a/vendor/github.com/codegangsta/cli/.travis.yml
+++ b/vendor/github.com/codegangsta/cli/.travis.yml
@@ -1,18 +1,40 @@
language: go
+
sudo: false
+cache:
+ directories:
+ - node_modules
+
go:
-- 1.1.2
- 1.2.2
- 1.3.3
-- 1.4.2
-- 1.5.1
-- tip
+- 1.4
+- 1.5.4
+- 1.6.2
+- master
matrix:
allow_failures:
- - go: tip
+ - go: master
+ include:
+ - go: 1.6.2
+ os: osx
+ - go: 1.1.2
+ install: go get -v .
+ before_script: echo skipping gfmxr on $TRAVIS_GO_VERSION
+ script:
+ - ./runtests vet
+ - ./runtests test
+
+before_script:
+- go get github.com/urfave/gfmxr/...
+- if [ ! -f node_modules/.bin/markdown-toc ] ; then
+ npm install markdown-toc ;
+ fi
script:
-- go vet ./...
-- go test -v ./...
+- ./runtests vet
+- ./runtests test
+- ./runtests gfmxr
+- ./runtests toc
diff --git a/vendor/github.com/codegangsta/cli/CHANGELOG.md b/vendor/github.com/codegangsta/cli/CHANGELOG.md
new file mode 100644
index 0000000..d1904fe
--- /dev/null
+++ b/vendor/github.com/codegangsta/cli/CHANGELOG.md
@@ -0,0 +1,329 @@
+# Change Log
+
+**ATTN**: This project uses [semantic versioning](http://semver.org/).
+
+## [Unreleased]
+
+## [1.18.0] - 2016-06-27
+### Added
+- `./runtests` test runner with coverage tracking by default
+- testing on OS X
+- testing on Windows
+- `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code
+
+### Changed
+- Use spaces for alignment in help/usage output instead of tabs, making the
+ output alignment consistent regardless of tab width
+
+### Fixed
+- Printing of command aliases in help text
+- Printing of visible flags for both struct and struct pointer flags
+- Display the `help` subcommand when using `CommandCategories`
+- No longer swallows `panic`s that occur within the `Action`s themselves when
+ detecting the signature of the `Action` field
+
+## [1.17.0] - 2016-05-09
+### Added
+- Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc`
+- `context.GlobalBoolT` was added as an analogue to `context.GlobalBool`
+- Support for hiding commands by setting `Hidden: true` -- this will hide the
+ commands in help output
+
+### Changed
+- `Float64Flag`, `IntFlag`, and `DurationFlag` default values are no longer
+ quoted in help text output.
+- All flag types now include `(default: {value})` strings following usage when a
+ default value can be (reasonably) detected.
+- `IntSliceFlag` and `StringSliceFlag` usage strings are now more consistent
+ with non-slice flag types
+- Apps now exit with a code of 3 if an unknown subcommand is specified
+ (previously they printed "No help topic for...", but still exited 0. This
+ makes it easier to script around apps built using `cli` since they can trust
+ that a 0 exit code indicated a successful execution.
+- cleanups based on [Go Report Card
+ feedback](https://goreportcard.com/report/github.com/urfave/cli)
+
+## [1.16.0] - 2016-05-02
+### Added
+- `Hidden` field on all flag struct types to omit from generated help text
+
+### Changed
+- `BashCompletionFlag` (`--enable-bash-completion`) is now omitted from
+generated help text via the `Hidden` field
+
+### Fixed
+- handling of error values in `HandleAction` and `HandleExitCoder`
+
+## [1.15.0] - 2016-04-30
+### Added
+- This file!
+- Support for placeholders in flag usage strings
+- `App.Metadata` map for arbitrary data/state management
+- `Set` and `GlobalSet` methods on `*cli.Context` for altering values after
+parsing.
+- Support for nested lookup of dot-delimited keys in structures loaded from
+YAML.
+
+### Changed
+- The `App.Action` and `Command.Action` now prefer a return signature of
+`func(*cli.Context) error`, as defined by `cli.ActionFunc`. If a non-nil
+`error` is returned, there may be two outcomes:
+ - If the error fulfills `cli.ExitCoder`, then `os.Exit` will be called
+ automatically
+ - Else the error is bubbled up and returned from `App.Run`
+- Specifying an `Action` with the legacy return signature of
+`func(*cli.Context)` will produce a deprecation message to stderr
+- Specifying an `Action` that is not a `func` type will produce a non-zero exit
+from `App.Run`
+- Specifying an `Action` func that has an invalid (input) signature will
+produce a non-zero exit from `App.Run`
+
+### Deprecated
+- <a name="deprecated-cli-app-runandexitonerror"></a>
+`cli.App.RunAndExitOnError`, which should now be done by returning an error
+that fulfills `cli.ExitCoder` to `cli.App.Run`.
+- <a name="deprecated-cli-app-action-signature"></a> the legacy signature for
+`cli.App.Action` of `func(*cli.Context)`, which should now have a return
+signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`.
+
+### Fixed
+- Added missing `*cli.Context.GlobalFloat64` method
+
+## [1.14.0] - 2016-04-03 (backfilled 2016-04-25)
+### Added
+- Codebeat badge
+- Support for categorization via `CategorizedHelp` and `Categories` on app.
+
+### Changed
+- Use `filepath.Base` instead of `path.Base` in `Name` and `HelpName`.
+
+### Fixed
+- Ensure version is not shown in help text when `HideVersion` set.
+
+## [1.13.0] - 2016-03-06 (backfilled 2016-04-25)
+### Added
+- YAML file input support.
+- `NArg` method on context.
+
+## [1.12.0] - 2016-02-17 (backfilled 2016-04-25)
+### Added
+- Custom usage error handling.
+- Custom text support in `USAGE` section of help output.
+- Improved help messages for empty strings.
+- AppVeyor CI configuration.
+
+### Changed
+- Removed `panic` from default help printer func.
+- De-duping and optimizations.
+
+### Fixed
+- Correctly handle `Before`/`After` at command level when no subcommands.
+- Case of literal `-` argument causing flag reordering.
+- Environment variable hints on Windows.
+- Docs updates.
+
+## [1.11.1] - 2015-12-21 (backfilled 2016-04-25)
+### Changed
+- Use `path.Base` in `Name` and `HelpName`
+- Export `GetName` on flag types.
+
+### Fixed
+- Flag parsing when skipping is enabled.
+- Test output cleanup.
+- Move completion check to account for empty input case.
+
+## [1.11.0] - 2015-11-15 (backfilled 2016-04-25)
+### Added
+- Destination scan support for flags.
+- Testing against `tip` in Travis CI config.
+
+### Changed
+- Go version in Travis CI config.
+
+### Fixed
+- Removed redundant tests.
+- Use correct example naming in tests.
+
+## [1.10.2] - 2015-10-29 (backfilled 2016-04-25)
+### Fixed
+- Remove unused var in bash completion.
+
+## [1.10.1] - 2015-10-21 (backfilled 2016-04-25)
+### Added
+- Coverage and reference logos in README.
+
+### Fixed
+- Use specified values in help and version parsing.
+- Only display app version and help message once.
+
+## [1.10.0] - 2015-10-06 (backfilled 2016-04-25)
+### Added
+- More tests for existing functionality.
+- `ArgsUsage` at app and command level for help text flexibility.
+
+### Fixed
+- Honor `HideHelp` and `HideVersion` in `App.Run`.
+- Remove juvenile word from README.
+
+## [1.9.0] - 2015-09-08 (backfilled 2016-04-25)
+### Added
+- `FullName` on command with accompanying help output update.
+- Set default `$PROG` in bash completion.
+
+### Changed
+- Docs formatting.
+
+### Fixed
+- Removed self-referential imports in tests.
+
+## [1.8.0] - 2015-06-30 (backfilled 2016-04-25)
+### Added
+- Support for `Copyright` at app level.
+- `Parent` func at context level to walk up context lineage.
+
+### Fixed
+- Global flag processing at top level.
+
+## [1.7.1] - 2015-06-11 (backfilled 2016-04-25)
+### Added
+- Aggregate errors from `Before`/`After` funcs.
+- Doc comments on flag structs.
+- Include non-global flags when checking version and help.
+- Travis CI config updates.
+
+### Fixed
+- Ensure slice type flags have non-nil values.
+- Collect global flags from the full command hierarchy.
+- Docs prose.
+
+## [1.7.0] - 2015-05-03 (backfilled 2016-04-25)
+### Changed
+- `HelpPrinter` signature includes output writer.
+
+### Fixed
+- Specify go 1.1+ in docs.
+- Set `Writer` when running command as app.
+
+## [1.6.0] - 2015-03-23 (backfilled 2016-04-25)
+### Added
+- Multiple author support.
+- `NumFlags` at context level.
+- `Aliases` at command level.
+
+### Deprecated
+- `ShortName` at command level.
+
+### Fixed
+- Subcommand help output.
+- Backward compatible support for deprecated `Author` and `Email` fields.
+- Docs regarding `Names`/`Aliases`.
+
+## [1.5.0] - 2015-02-20 (backfilled 2016-04-25)
+### Added
+- `After` hook func support at app and command level.
+
+### Fixed
+- Use parsed context when running command as subcommand.
+- Docs prose.
+
+## [1.4.1] - 2015-01-09 (backfilled 2016-04-25)
+### Added
+- Support for hiding `-h / --help` flags, but not `help` subcommand.
+- Stop flag parsing after `--`.
+
+### Fixed
+- Help text for generic flags to specify single value.
+- Use double quotes in output for defaults.
+- Use `ParseInt` instead of `ParseUint` for int environment var values.
+- Use `0` as base when parsing int environment var values.
+
+## [1.4.0] - 2014-12-12 (backfilled 2016-04-25)
+### Added
+- Support for environment variable lookup "cascade".
+- Support for `Stdout` on app for output redirection.
+
+### Fixed
+- Print command help instead of app help in `ShowCommandHelp`.
+
+## [1.3.1] - 2014-11-13 (backfilled 2016-04-25)
+### Added
+- Docs and example code updates.
+
+### Changed
+- Default `-v / --version` flag made optional.
+
+## [1.3.0] - 2014-08-10 (backfilled 2016-04-25)
+### Added
+- `FlagNames` at context level.
+- Exposed `VersionPrinter` var for more control over version output.
+- Zsh completion hook.
+- `AUTHOR` section in default app help template.
+- Contribution guidelines.
+- `DurationFlag` type.
+
+## [1.2.0] - 2014-08-02
+### Added
+- Support for environment variable defaults on flags plus tests.
+
+## [1.1.0] - 2014-07-15
+### Added
+- Bash completion.
+- Optional hiding of built-in help command.
+- Optional skipping of flag parsing at command level.
+- `Author`, `Email`, and `Compiled` metadata on app.
+- `Before` hook func support at app and command level.
+- `CommandNotFound` func support at app level.
+- Command reference available on context.
+- `GenericFlag` type.
+- `Float64Flag` type.
+- `BoolTFlag` type.
+- `IsSet` flag helper on context.
+- More flag lookup funcs at context level.
+- More tests & docs.
+
+### Changed
+- Help template updates to account for presence/absence of flags.
+- Separated subcommand help template.
+- Exposed `HelpPrinter` var for more control over help output.
+
+## [1.0.0] - 2013-11-01
+### Added
+- `help` flag in default app flag set and each command flag set.
+- Custom handling of argument parsing errors.
+- Command lookup by name at app level.
+- `StringSliceFlag` type and supporting `StringSlice` type.
+- `IntSliceFlag` type and supporting `IntSlice` type.
+- Slice type flag lookups by name at context level.
+- Export of app and command help functions.
+- More tests & docs.
+
+## 0.1.0 - 2013-07-22
+### Added
+- Initial implementation.
+
+[Unreleased]: https://github.com/urfave/cli/compare/v1.18.0...HEAD
+[1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0
+[1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0
+[1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0
+[1.15.0]: https://github.com/urfave/cli/compare/v1.14.0...v1.15.0
+[1.14.0]: https://github.com/urfave/cli/compare/v1.13.0...v1.14.0
+[1.13.0]: https://github.com/urfave/cli/compare/v1.12.0...v1.13.0
+[1.12.0]: https://github.com/urfave/cli/compare/v1.11.1...v1.12.0
+[1.11.1]: https://github.com/urfave/cli/compare/v1.11.0...v1.11.1
+[1.11.0]: https://github.com/urfave/cli/compare/v1.10.2...v1.11.0
+[1.10.2]: https://github.com/urfave/cli/compare/v1.10.1...v1.10.2
+[1.10.1]: https://github.com/urfave/cli/compare/v1.10.0...v1.10.1
+[1.10.0]: https://github.com/urfave/cli/compare/v1.9.0...v1.10.0
+[1.9.0]: https://github.com/urfave/cli/compare/v1.8.0...v1.9.0
+[1.8.0]: https://github.com/urfave/cli/compare/v1.7.1...v1.8.0
+[1.7.1]: https://github.com/urfave/cli/compare/v1.7.0...v1.7.1
+[1.7.0]: https://github.com/urfave/cli/compare/v1.6.0...v1.7.0
+[1.6.0]: https://github.com/urfave/cli/compare/v1.5.0...v1.6.0
+[1.5.0]: https://github.com/urfave/cli/compare/v1.4.1...v1.5.0
+[1.4.1]: https://github.com/urfave/cli/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/urfave/cli/compare/v1.3.1...v1.4.0
+[1.3.1]: https://github.com/urfave/cli/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/urfave/cli/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/urfave/cli/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/urfave/cli/compare/v1.0.0...v1.1.0
+[1.0.0]: https://github.com/urfave/cli/compare/v0.1.0...v1.0.0
diff --git a/vendor/github.com/codegangsta/cli/LICENSE b/vendor/github.com/codegangsta/cli/LICENSE
index 5515ccf..42a597e 100644
--- a/vendor/github.com/codegangsta/cli/LICENSE
+++ b/vendor/github.com/codegangsta/cli/LICENSE
@@ -1,21 +1,21 @@
-Copyright (C) 2013 Jeremy Saenz
-All Rights Reserved.
+MIT License
-MIT LICENSE
+Copyright (c) 2016 Jeremy Saenz & Contributors
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/codegangsta/cli/README.md b/vendor/github.com/codegangsta/cli/README.md
index d9371cf..ebb1d74 100644
--- a/vendor/github.com/codegangsta/cli/README.md
+++ b/vendor/github.com/codegangsta/cli/README.md
@@ -1,42 +1,145 @@
-[](http://gocover.io/github.com/codegangsta/cli)
-[](https://travis-ci.org/codegangsta/cli)
-[](https://godoc.org/github.com/codegangsta/cli)
-[](https://codebeat.co/projects/github-com-codegangsta-cli)
+cli
+===
-# cli.go
+[](https://travis-ci.org/urfave/cli)
+[](https://ci.appveyor.com/project/urfave/cli)
+[](https://godoc.org/github.com/urfave/cli)
+[](https://codebeat.co/projects/github-com-urfave-cli)
+[](https://goreportcard.com/report/urfave/cli)
+[](http://gocover.io/github.com/urfave/cli) /
+[](http://gocover.io/github.com/urfave/cli/altsrc)
-`cli.go` is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way.
+**Notice:** This is the library formerly known as
+`github.com/codegangsta/cli` -- Github will automatically redirect requests
+to this repository, but we recommend updating your references for clarity.
+
+cli is a simple, fast, and fun package for building command line apps in Go. The
+goal is to enable developers to write fast and distributable command line
+applications in an expressive way.
+
+<!-- toc -->
+
+- [Overview](#overview)
+- [Installation](#installation)
+ * [Supported platforms](#supported-platforms)
+ * [Using the `v2` branch](#using-the-v2-branch)
+ * [Pinning to the `v1` branch](#pinning-to-the-v1-branch)
+- [Getting Started](#getting-started)
+- [Examples](#examples)
+ * [Arguments](#arguments)
+ * [Flags](#flags)
+ + [Placeholder Values](#placeholder-values)
+ + [Alternate Names](#alternate-names)
+ + [Values from the Environment](#values-from-the-environment)
+ + [Values from alternate input sources (YAML and others)](#values-from-alternate-input-sources-yaml-and-others)
+ * [Subcommands](#subcommands)
+ * [Subcommands categories](#subcommands-categories)
+ * [Exit code](#exit-code)
+ * [Bash Completion](#bash-completion)
+ + [Enabling](#enabling)
+ + [Distribution](#distribution)
+ + [Customization](#customization)
+ * [Generated Help Text](#generated-help-text)
+ + [Customization](#customization-1)
+ * [Version Flag](#version-flag)
+ + [Customization](#customization-2)
+ + [Full API Example](#full-api-example)
+- [Contribution Guidelines](#contribution-guidelines)
+
+<!-- tocstop -->
## Overview
-Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app.
+Command line apps are usually so tiny that there is absolutely no reason why
+your code should *not* be self-documenting. Things like generating help text and
+parsing command flags/options should not hinder productivity when writing a
+command line app.
-**This is where `cli.go` comes into play.** `cli.go` makes command line programming fun, organized, and expressive!
+**This is where cli comes into play.** cli makes command line programming fun,
+organized, and expressive!
## Installation
-Make sure you have a working Go environment (go 1.1+ is *required*). [See the install instructions](http://golang.org/doc/install.html).
+Make sure you have a working Go environment. Go version 1.1+ is required for
+core cli, whereas use of the [`./altsrc`](./altsrc) input extensions requires Go
+version 1.2+. [See the install
+instructions](http://golang.org/doc/install.html).
-To install `cli.go`, simply run:
+To install cli, simply run:
```
-$ go get github.com/codegangsta/cli
+$ go get github.com/urfave/cli
```
-Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used:
+Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands
+can be easily used:
```
export PATH=$PATH:$GOPATH/bin
```
+### Supported platforms
+
+cli is tested against multiple versions of Go on Linux, and against the latest
+released version of Go on OS X and Windows. For full details, see
+[`./.travis.yml`](./.travis.yml) and [`./appveyor.yml`](./appveyor.yml).
+
+### Using the `v2` branch
+
+**Warning**: The `v2` branch is currently unreleased and considered unstable.
+
+There is currently a long-lived branch named `v2` that is intended to land as
+the new `master` branch once development there has settled down. The current
+`master` branch (mirrored as `v1`) is being manually merged into `v2` on
+an irregular human-based schedule, but generally if one wants to "upgrade" to
+`v2` *now* and accept the volatility (read: "awesomeness") that comes along with
+that, please use whatever version pinning of your preference, such as via
+`gopkg.in`:
+
+```
+$ go get gopkg.in/urfave/cli.v2
+```
+
+``` go
+...
+import (
+ "gopkg.in/urfave/cli.v2" // imports as package "cli"
+)
+...
+```
+
+### Pinning to the `v1` branch
+
+Similarly to the section above describing use of the `v2` branch, if one wants
+to avoid any unexpected compatibility pains once `v2` becomes `master`, then
+pinning to the `v1` branch is an acceptable option, e.g.:
+
+```
+$ go get gopkg.in/urfave/cli.v1
+```
+
+``` go
+...
+import (
+ "gopkg.in/urfave/cli.v1" // imports as package "cli"
+)
+...
+```
+
## Getting Started
-One of the philosophies behind `cli.go` is that an API should be playful and full of discovery. So a `cli.go` app can be as little as one line of code in `main()`.
+One of the philosophies behind cli is that an API should be playful and full of
+discovery. So a cli app can be as little as one line of code in `main()`.
+<!-- {
+ "args": ["--help"],
+ "output": "A new cli application"
+} -->
``` go
package main
import (
"os"
- "github.com/codegangsta/cli"
+
+ "github.com/urfave/cli"
)
func main() {
@@ -44,50 +147,67 @@
}
```
-This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation:
+This app will run and show help text, but is not very useful. Let's give an
+action to execute and some help documentation:
+<!-- {
+ "output": "boom! I say!"
+} -->
``` go
package main
import (
+ "fmt"
"os"
- "github.com/codegangsta/cli"
+
+ "github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "boom"
app.Usage = "make an explosive entrance"
- app.Action = func(c *cli.Context) {
- println("boom! I say!")
+ app.Action = func(c *cli.Context) error {
+ fmt.Println("boom! I say!")
+ return nil
}
app.Run(os.Args)
}
```
-Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below.
+Running this already gives you a ton of functionality, plus support for things
+like subcommands and flags, which are covered below.
-## Example
+## Examples
-Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness!
+Being a programmer can be a lonely job. Thankfully by the power of automation
+that is not the case! Let's create a greeter app to fend off our demons of
+loneliness!
-Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it:
+Start by creating a directory named `greet`, and within it, add a file,
+`greet.go` with the following code in it:
+<!-- {
+ "output": "Hello friend!"
+} -->
``` go
package main
import (
+ "fmt"
"os"
- "github.com/codegangsta/cli"
+
+ "github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "greet"
app.Usage = "fight the loneliness!"
- app.Action = func(c *cli.Context) {
- println("Hello friend!")
+ app.Action = func(c *cli.Context) error {
+ fmt.Println("Hello friend!")
+ return nil
}
app.Run(os.Args)
@@ -107,7 +227,7 @@
Hello friend!
```
-`cli.go` also generates neat help text:
+cli also generates neat help text:
```
$ greet help
@@ -124,209 +244,410 @@
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS
- --version Shows version information
+ --version Shows version information
```
### Arguments
-You can lookup arguments by calling the `Args` function on `cli.Context`.
+You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.:
+<!-- {
+ "output": "Hello \""
+} -->
``` go
-...
-app.Action = func(c *cli.Context) {
- println("Hello", c.Args()[0])
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ app := cli.NewApp()
+
+ app.Action = func(c *cli.Context) error {
+ fmt.Printf("Hello %q", c.Args().Get(0))
+ return nil
+ }
+
+ app.Run(os.Args)
}
-...
```
### Flags
Setting and querying flags is simple.
+<!-- {
+ "output": "Hello Nefertiti"
+} -->
``` go
-...
-app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang",
- Value: "english",
- Usage: "language for the greeting",
- },
-}
-app.Action = func(c *cli.Context) {
- name := "someone"
- if c.NArg() > 0 {
- name = c.Args()[0]
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ app := cli.NewApp()
+
+ app.Flags = []cli.Flag {
+ cli.StringFlag{
+ Name: "lang",
+ Value: "english",
+ Usage: "language for the greeting",
+ },
}
- if c.String("lang") == "spanish" {
- println("Hola", name)
- } else {
- println("Hello", name)
+
+ app.Action = func(c *cli.Context) error {
+ name := "Nefertiti"
+ if c.NArg() > 0 {
+ name = c.Args().Get(0)
+ }
+ if c.String("lang") == "spanish" {
+ fmt.Println("Hola", name)
+ } else {
+ fmt.Println("Hello", name)
+ }
+ return nil
}
+
+ app.Run(os.Args)
}
-...
```
-You can also set a destination variable for a flag, to which the content will be scanned.
+You can also set a destination variable for a flag, to which the content will be
+scanned.
+<!-- {
+ "output": "Hello someone"
+} -->
``` go
-...
-var language string
-app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang",
- Value: "english",
- Usage: "language for the greeting",
- Destination: &language,
- },
-}
-app.Action = func(c *cli.Context) {
- name := "someone"
- if c.NArg() > 0 {
- name = c.Args()[0]
+package main
+
+import (
+ "os"
+ "fmt"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ var language string
+
+ app := cli.NewApp()
+
+ app.Flags = []cli.Flag {
+ cli.StringFlag{
+ Name: "lang",
+ Value: "english",
+ Usage: "language for the greeting",
+ Destination: &language,
+ },
}
- if language == "spanish" {
- println("Hola", name)
- } else {
- println("Hello", name)
+
+ app.Action = func(c *cli.Context) error {
+ name := "someone"
+ if c.NArg() > 0 {
+ name = c.Args()[0]
+ }
+ if language == "spanish" {
+ fmt.Println("Hola", name)
+ } else {
+ fmt.Println("Hello", name)
+ }
+ return nil
}
+
+ app.Run(os.Args)
}
-...
```
-See full list of flags at http://godoc.org/github.com/codegangsta/cli
+See full list of flags at http://godoc.org/github.com/urfave/cli
+
+#### Placeholder Values
+
+Sometimes it's useful to specify a flag's value within the usage string itself.
+Such placeholders are indicated with back quotes.
+
+For example this:
+
+<!-- {
+ "args": ["--help"],
+ "output": "--config FILE, -c FILE"
+} -->
+```go
+package main
+
+import (
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ app := cli.NewApp()
+
+ app.Flags = []cli.Flag{
+ cli.StringFlag{
+ Name: "config, c",
+ Usage: "Load configuration from `FILE`",
+ },
+ }
+
+ app.Run(os.Args)
+}
+```
+
+Will result in help output like:
+
+```
+--config FILE, -c FILE Load configuration from FILE
+```
+
+Note that only the first placeholder is used. Subsequent back-quoted words will
+be left as-is.
#### Alternate Names
-You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g.
+You can set alternate (or short) names for flags by providing a comma-delimited
+list for the `Name`. e.g.
+<!-- {
+ "args": ["--help"],
+ "output": "--lang value, -l value.*language for the greeting.*default: \"english\""
+} -->
``` go
-app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang, l",
- Value: "english",
- Usage: "language for the greeting",
- },
+package main
+
+import (
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ app := cli.NewApp()
+
+ app.Flags = []cli.Flag {
+ cli.StringFlag{
+ Name: "lang, l",
+ Value: "english",
+ Usage: "language for the greeting",
+ },
+ }
+
+ app.Run(os.Args)
}
```
-That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error.
+That flag can then be set with `--lang spanish` or `-l spanish`. Note that
+giving two different forms of the same flag in the same command invocation is an
+error.
#### Values from the Environment
You can also have the default value set from the environment via `EnvVar`. e.g.
+<!-- {
+ "args": ["--help"],
+ "output": "language for the greeting.*APP_LANG"
+} -->
``` go
-app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang, l",
- Value: "english",
- Usage: "language for the greeting",
- EnvVar: "APP_LANG",
- },
+package main
+
+import (
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ app := cli.NewApp()
+
+ app.Flags = []cli.Flag {
+ cli.StringFlag{
+ Name: "lang, l",
+ Value: "english",
+ Usage: "language for the greeting",
+ EnvVar: "APP_LANG",
+ },
+ }
+
+ app.Run(os.Args)
}
```
-The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default.
+The `EnvVar` may also be given as a comma-delimited "cascade", where the first
+environment variable that resolves is used as the default.
+<!-- {
+ "args": ["--help"],
+ "output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG"
+} -->
``` go
-app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang, l",
- Value: "english",
- Usage: "language for the greeting",
- EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
- },
+package main
+
+import (
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ app := cli.NewApp()
+
+ app.Flags = []cli.Flag {
+ cli.StringFlag{
+ Name: "lang, l",
+ Value: "english",
+ Usage: "language for the greeting",
+ EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
+ },
+ }
+
+ app.Run(os.Args)
}
```
#### Values from alternate input sources (YAML and others)
-There is a separate package altsrc that adds support for getting flag values from other input sources like YAML.
+There is a separate package altsrc that adds support for getting flag values
+from other input sources like YAML.
-In order to get values for a flag from an alternate input source the following code would be added to wrap an existing cli.Flag like below:
+In order to get values for a flag from an alternate input source the following
+code would be added to wrap an existing cli.Flag like below:
``` go
altsrc.NewIntFlag(cli.IntFlag{Name: "test"})
```
-Initialization must also occur for these flags. Below is an example initializing getting data from a yaml file below.
+Initialization must also occur for these flags. Below is an example initializing
+getting data from a yaml file below.
``` go
command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
```
-The code above will use the "load" string as a flag name to get the file name of a yaml file from the cli.Context.
-It will then use that file name to initialize the yaml input source for any flags that are defined on that command.
-As a note the "load" flag used would also have to be defined on the command flags in order for this code snipped to work.
+The code above will use the "load" string as a flag name to get the file name of
+a yaml file from the cli.Context. It will then use that file name to initialize
+the yaml input source for any flags that are defined on that command. As a note
+the "load" flag used would also have to be defined on the command flags in order
+for this code snipped to work.
-Currently only YAML files are supported but developers can add support for other input sources by implementing the
-altsrc.InputSourceContext for their given sources.
+Currently only YAML files are supported but developers can add support for other
+input sources by implementing the altsrc.InputSourceContext for their given
+sources.
Here is a more complete sample of a command using YAML support:
+<!-- {
+ "args": ["test-cmd", "--help"],
+ "output": "--test value.*default: 0"
+} -->
``` go
- command := &cli.Command{
- Name: "test-cmd",
- Aliases: []string{"tc"},
- Usage: "this is for testing",
- Description: "testing",
- Action: func(c *cli.Context) {
- // Action to run
- },
- Flags: []cli.Flag{
- NewIntFlag(cli.IntFlag{Name: "test"}),
- cli.StringFlag{Name: "load"}},
- }
- command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
- err := command.Run(c)
+package notmain
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/urfave/cli"
+ "github.com/urfave/cli/altsrc"
+)
+
+func main() {
+ app := cli.NewApp()
+
+ flags := []cli.Flag{
+ altsrc.NewIntFlag(cli.IntFlag{Name: "test"}),
+ cli.StringFlag{Name: "load"},
+ }
+
+ app.Action = func(c *cli.Context) error {
+ fmt.Println("yaml ist rad")
+ return nil
+ }
+
+ app.Before = altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load"))
+ app.Flags = flags
+
+ app.Run(os.Args)
+}
```
### Subcommands
Subcommands can be defined for a more git-like command line app.
+<!-- {
+ "args": ["template", "add"],
+ "output": "new task template: .+"
+} -->
```go
-...
-app.Commands = []cli.Command{
- {
- Name: "add",
- Aliases: []string{"a"},
- Usage: "add a task to the list",
- Action: func(c *cli.Context) {
- println("added task: ", c.Args().First())
- },
- },
- {
- Name: "complete",
- Aliases: []string{"c"},
- Usage: "complete a task on the list",
- Action: func(c *cli.Context) {
- println("completed task: ", c.Args().First())
- },
- },
- {
- Name: "template",
- Aliases: []string{"r"},
- Usage: "options for task templates",
- Subcommands: []cli.Command{
- {
- Name: "add",
- Usage: "add a new template",
- Action: func(c *cli.Context) {
- println("new task template: ", c.Args().First())
- },
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ app := cli.NewApp()
+
+ app.Commands = []cli.Command{
+ {
+ Name: "add",
+ Aliases: []string{"a"},
+ Usage: "add a task to the list",
+ Action: func(c *cli.Context) error {
+ fmt.Println("added task: ", c.Args().First())
+ return nil
},
- {
- Name: "remove",
- Usage: "remove an existing template",
- Action: func(c *cli.Context) {
- println("removed task template: ", c.Args().First())
+ },
+ {
+ Name: "complete",
+ Aliases: []string{"c"},
+ Usage: "complete a task on the list",
+ Action: func(c *cli.Context) error {
+ fmt.Println("completed task: ", c.Args().First())
+ return nil
+ },
+ },
+ {
+ Name: "template",
+ Aliases: []string{"t"},
+ Usage: "options for task templates",
+ Subcommands: []cli.Command{
+ {
+ Name: "add",
+ Usage: "add a new template",
+ Action: func(c *cli.Context) error {
+ fmt.Println("new task template: ", c.Args().First())
+ return nil
+ },
+ },
+ {
+ Name: "remove",
+ Usage: "remove an existing template",
+ Action: func(c *cli.Context) error {
+ fmt.Println("removed task template: ", c.Args().First())
+ return nil
+ },
},
},
},
- },
+ }
+
+ app.Run(os.Args)
}
-...
```
### Subcommands categories
@@ -338,34 +659,79 @@
E.g.
```go
-...
- app.Commands = []cli.Command{
- {
- Name: "noop",
- },
- {
- Name: "add",
- Category: "template",
- },
- {
- Name: "remove",
- Category: "template",
- },
- }
-...
+package main
+
+import (
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ app := cli.NewApp()
+
+ app.Commands = []cli.Command{
+ {
+ Name: "noop",
+ },
+ {
+ Name: "add",
+ Category: "template",
+ },
+ {
+ Name: "remove",
+ Category: "template",
+ },
+ }
+
+ app.Run(os.Args)
+}
```
Will include:
```
-...
COMMANDS:
noop
Template actions:
add
remove
-...
+```
+
+### Exit code
+
+Calling `App.Run` will not automatically call `os.Exit`, which means that by
+default the exit code will "fall through" to being `0`. An explicit exit code
+may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
+`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
+
+``` go
+package main
+
+import (
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ app := cli.NewApp()
+ app.Flags = []cli.Flag{
+ cli.BoolTFlag{
+ Name: "ginger-crouton",
+ Usage: "is it in the soup?",
+ },
+ }
+ app.Action = func(ctx *cli.Context) error {
+ if !ctx.Bool("ginger-crouton") {
+ return cli.NewExitError("it is not in the soup", 86)
+ }
+ return nil
+ }
+
+ app.Run(os.Args)
+}
```
### Bash Completion
@@ -375,41 +741,58 @@
show an app's subcommands, but you can write your own completion methods for
the App or its subcommands.
-```go
-...
-var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
-app := cli.NewApp()
-app.EnableBashCompletion = true
-app.Commands = []cli.Command{
- {
- Name: "complete",
- Aliases: []string{"c"},
- Usage: "complete a task on the list",
- Action: func(c *cli.Context) {
- println("completed task: ", c.Args().First())
- },
- BashComplete: func(c *cli.Context) {
- // This will complete if no args are passed
- if c.NArg() > 0 {
- return
- }
- for _, t := range tasks {
- fmt.Println(t)
- }
+<!-- {
+ "args": ["complete", "--generate-bash-completion"],
+ "output": "laundry"
+} -->
+``` go
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
+
+ app := cli.NewApp()
+ app.EnableBashCompletion = true
+ app.Commands = []cli.Command{
+ {
+ Name: "complete",
+ Aliases: []string{"c"},
+ Usage: "complete a task on the list",
+ Action: func(c *cli.Context) error {
+ fmt.Println("completed task: ", c.Args().First())
+ return nil
+ },
+ BashComplete: func(c *cli.Context) {
+ // This will complete if no args are passed
+ if c.NArg() > 0 {
+ return
+ }
+ for _, t := range tasks {
+ fmt.Println(t)
+ }
+ },
},
}
+
+ app.Run(os.Args)
}
-...
```
-#### To Enable
+#### Enabling
Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
setting the `PROG` variable to the name of your program:
`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
-#### To Distribute
+#### Distribution
Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
it to the name of the program you wish to add autocomplete support for (or
@@ -425,10 +808,502 @@
`autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set
to the name of their program (as above).
+#### Customization
+
+The default bash completion flag (`--generate-bash-completion`) is defined as
+`cli.BashCompletionFlag`, and may be redefined if desired, e.g.:
+
+<!-- {
+ "args": ["--compgen"],
+ "output": "wat\nhelp\nh"
+} -->
+``` go
+package main
+
+import (
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ cli.BashCompletionFlag = cli.BoolFlag{
+ Name: "compgen",
+ Hidden: true,
+ }
+
+ app := cli.NewApp()
+ app.EnableBashCompletion = true
+ app.Commands = []cli.Command{
+ {
+ Name: "wat",
+ },
+ }
+ app.Run(os.Args)
+}
+```
+
+### Generated Help Text
+
+The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked
+by the cli internals in order to print generated help text for the app, command,
+or subcommand, and break execution.
+
+#### Customization
+
+All of the help text generation may be customized, and at multiple levels. The
+templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
+`SubcommandHelpTemplate` which may be reassigned or augmented, and full override
+is possible by assigning a compatible func to the `cli.HelpPrinter` variable,
+e.g.:
+
+<!-- {
+ "output": "Ha HA. I pwnd the help!!1"
+} -->
+``` go
+package main
+
+import (
+ "fmt"
+ "io"
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ // EXAMPLE: Append to an existing template
+ cli.AppHelpTemplate = fmt.Sprintf(`%s
+
+WEBSITE: http://awesometown.example.com
+
+SUPPORT: support@awesometown.example.com
+
+`, cli.AppHelpTemplate)
+
+ // EXAMPLE: Override a template
+ cli.AppHelpTemplate = `NAME:
+ {{.Name}} - {{.Usage}}
+USAGE:
+ {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command
+[command options]{{end}} {{if
+.ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
+ {{if len .Authors}}
+AUTHOR(S):
+ {{range .Authors}}{{ . }}{{end}}
+ {{end}}{{if .Commands}}
+COMMANDS:
+{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"
+}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
+GLOBAL OPTIONS:
+ {{range .VisibleFlags}}{{.}}
+ {{end}}{{end}}{{if .Copyright }}
+COPYRIGHT:
+ {{.Copyright}}
+ {{end}}{{if .Version}}
+VERSION:
+ {{.Version}}
+ {{end}}
+`
+
+ // EXAMPLE: Replace the `HelpPrinter` func
+ cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
+ fmt.Println("Ha HA. I pwnd the help!!1")
+ }
+
+ cli.NewApp().Run(os.Args)
+}
+```
+
+The default flag may be customized to something other than `-h/--help` by
+setting `cli.HelpFlag`, e.g.:
+
+<!-- {
+ "args": ["--halp"],
+ "output": "haaaaalp.*HALP"
+} -->
+``` go
+package main
+
+import (
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ cli.HelpFlag = cli.BoolFlag{
+ Name: "halp, haaaaalp",
+ Usage: "HALP",
+ EnvVar: "SHOW_HALP,HALPPLZ",
+ }
+
+ cli.NewApp().Run(os.Args)
+}
+```
+
+### Version Flag
+
+The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which
+is checked by the cli internals in order to print the `App.Version` via
+`cli.VersionPrinter` and break execution.
+
+#### Customization
+
+The default flag may be cusomized to something other than `-v/--version` by
+setting `cli.VersionFlag`, e.g.:
+
+<!-- {
+ "args": ["--print-version"],
+ "output": "partay version v19\\.99\\.0"
+} -->
+``` go
+package main
+
+import (
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+func main() {
+ cli.VersionFlag = cli.BoolFlag{
+ Name: "print-version, V",
+ Usage: "print only the version",
+ }
+
+ app := cli.NewApp()
+ app.Name = "partay"
+ app.Version = "v19.99.0"
+ app.Run(os.Args)
+}
+```
+
+Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.:
+
+<!-- {
+ "args": ["--version"],
+ "output": "version=v19\\.99\\.0 revision=fafafaf"
+} -->
+``` go
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/urfave/cli"
+)
+
+var (
+ Revision = "fafafaf"
+)
+
+func main() {
+ cli.VersionPrinter = func(c *cli.Context) {
+ fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
+ }
+
+ app := cli.NewApp()
+ app.Name = "partay"
+ app.Version = "v19.99.0"
+ app.Run(os.Args)
+}
+```
+
+#### Full API Example
+
+**Notice**: This is a contrived (functioning) example meant strictly for API
+demonstration purposes. Use of one's imagination is encouraged.
+
+<!-- {
+ "output": "made it!\nPhew!"
+} -->
+``` go
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "time"
+
+ "github.com/urfave/cli"
+)
+
+func init() {
+ cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n"
+ cli.CommandHelpTemplate += "\nYMMV\n"
+ cli.SubcommandHelpTemplate += "\nor something\n"
+
+ cli.HelpFlag = cli.BoolFlag{Name: "halp"}
+ cli.BashCompletionFlag = cli.BoolFlag{Name: "compgen", Hidden: true}
+ cli.VersionFlag = cli.BoolFlag{Name: "print-version, V"}
+
+ cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
+ fmt.Fprintf(w, "best of luck to you\n")
+ }
+ cli.VersionPrinter = func(c *cli.Context) {
+ fmt.Fprintf(c.App.Writer, "version=%s\n", c.App.Version)
+ }
+ cli.OsExiter = func(c int) {
+ fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", c)
+ }
+ cli.ErrWriter = ioutil.Discard
+ cli.FlagStringer = func(fl cli.Flag) string {
+ return fmt.Sprintf("\t\t%s", fl.GetName())
+ }
+}
+
+type hexWriter struct{}
+
+func (w *hexWriter) Write(p []byte) (int, error) {
+ for _, b := range p {
+ fmt.Printf("%x", b)
+ }
+ fmt.Printf("\n")
+
+ return len(p), nil
+}
+
+type genericType struct{
+ s string
+}
+
+func (g *genericType) Set(value string) error {
+ g.s = value
+ return nil
+}
+
+func (g *genericType) String() string {
+ return g.s
+}
+
+func main() {
+ app := cli.NewApp()
+ app.Name = "kənˈtrīv"
+ app.Version = "v19.99.0"
+ app.Compiled = time.Now()
+ app.Authors = []cli.Author{
+ cli.Author{
+ Name: "Example Human",
+ Email: "human@example.com",
+ },
+ }
+ app.Copyright = "(c) 1999 Serious Enterprise"
+ app.HelpName = "contrive"
+ app.Usage = "demonstrate available API"
+ app.UsageText = "contrive - demonstrating the available API"
+ app.ArgsUsage = "[args and such]"
+ app.Commands = []cli.Command{
+ cli.Command{
+ Name: "doo",
+ Aliases: []string{"do"},
+ Category: "motion",
+ Usage: "do the doo",
+ UsageText: "doo - does the dooing",
+ Description: "no really, there is a lot of dooing to be done",
+ ArgsUsage: "[arrgh]",
+ Flags: []cli.Flag{
+ cli.BoolFlag{Name: "forever, forevvarr"},
+ },
+ Subcommands: cli.Commands{
+ cli.Command{
+ Name: "wop",
+ Action: wopAction,
+ },
+ },
+ SkipFlagParsing: false,
+ HideHelp: false,
+ Hidden: false,
+ HelpName: "doo!",
+ BashComplete: func(c *cli.Context) {
+ fmt.Fprintf(c.App.Writer, "--better\n")
+ },
+ Before: func(c *cli.Context) error {
+ fmt.Fprintf(c.App.Writer, "brace for impact\n")
+ return nil
+ },
+ After: func(c *cli.Context) error {
+ fmt.Fprintf(c.App.Writer, "did we lose anyone?\n")
+ return nil
+ },
+ Action: func(c *cli.Context) error {
+ c.Command.FullName()
+ c.Command.HasName("wop")
+ c.Command.Names()
+ c.Command.VisibleFlags()
+ fmt.Fprintf(c.App.Writer, "dodododododoodododddooooododododooo\n")
+ if c.Bool("forever") {
+ c.Command.Run(c)
+ }
+ return nil
+ },
+ OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error {
+ fmt.Fprintf(c.App.Writer, "for shame\n")
+ return err
+ },
+ },
+ }
+ app.Flags = []cli.Flag{
+ cli.BoolFlag{Name: "fancy"},
+ cli.BoolTFlag{Name: "fancier"},
+ cli.DurationFlag{Name: "howlong, H", Value: time.Second * 3},
+ cli.Float64Flag{Name: "howmuch"},
+ cli.GenericFlag{Name: "wat", Value: &genericType{}},
+ cli.Int64Flag{Name: "longdistance"},
+ cli.Int64SliceFlag{Name: "intervals"},
+ cli.IntFlag{Name: "distance"},
+ cli.IntSliceFlag{Name: "times"},
+ cli.StringFlag{Name: "dance-move, d"},
+ cli.StringSliceFlag{Name: "names, N"},
+ cli.UintFlag{Name: "age"},
+ cli.Uint64Flag{Name: "bigage"},
+ }
+ app.EnableBashCompletion = true
+ app.HideHelp = false
+ app.HideVersion = false
+ app.BashComplete = func(c *cli.Context) {
+ fmt.Fprintf(c.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n")
+ }
+ app.Before = func(c *cli.Context) error {
+ fmt.Fprintf(c.App.Writer, "HEEEERE GOES\n")
+ return nil
+ }
+ app.After = func(c *cli.Context) error {
+ fmt.Fprintf(c.App.Writer, "Phew!\n")
+ return nil
+ }
+ app.CommandNotFound = func(c *cli.Context, command string) {
+ fmt.Fprintf(c.App.Writer, "Thar be no %q here.\n", command)
+ }
+ app.OnUsageError = func(c *cli.Context, err error, isSubcommand bool) error {
+ if isSubcommand {
+ return err
+ }
+
+ fmt.Fprintf(c.App.Writer, "WRONG: %#v\n", err)
+ return nil
+ }
+ app.Action = func(c *cli.Context) error {
+ cli.DefaultAppComplete(c)
+ cli.HandleExitCoder(errors.New("not an exit coder, though"))
+ cli.ShowAppHelp(c)
+ cli.ShowCommandCompletions(c, "nope")
+ cli.ShowCommandHelp(c, "also-nope")
+ cli.ShowCompletions(c)
+ cli.ShowSubcommandHelp(c)
+ cli.ShowVersion(c)
+
+ categories := c.App.Categories()
+ categories.AddCommand("sounds", cli.Command{
+ Name: "bloop",
+ })
+
+ for _, category := range c.App.Categories() {
+ fmt.Fprintf(c.App.Writer, "%s\n", category.Name)
+ fmt.Fprintf(c.App.Writer, "%#v\n", category.Commands)
+ fmt.Fprintf(c.App.Writer, "%#v\n", category.VisibleCommands())
+ }
+
+ fmt.Printf("%#v\n", c.App.Command("doo"))
+ if c.Bool("infinite") {
+ c.App.Run([]string{"app", "doo", "wop"})
+ }
+
+ if c.Bool("forevar") {
+ c.App.RunAsSubcommand(c)
+ }
+ c.App.Setup()
+ fmt.Printf("%#v\n", c.App.VisibleCategories())
+ fmt.Printf("%#v\n", c.App.VisibleCommands())
+ fmt.Printf("%#v\n", c.App.VisibleFlags())
+
+ fmt.Printf("%#v\n", c.Args().First())
+ if len(c.Args()) > 0 {
+ fmt.Printf("%#v\n", c.Args()[1])
+ }
+ fmt.Printf("%#v\n", c.Args().Present())
+ fmt.Printf("%#v\n", c.Args().Tail())
+
+ set := flag.NewFlagSet("contrive", 0)
+ nc := cli.NewContext(c.App, set, c)
+
+ fmt.Printf("%#v\n", nc.Args())
+ fmt.Printf("%#v\n", nc.Bool("nope"))
+ fmt.Printf("%#v\n", nc.BoolT("nerp"))
+ fmt.Printf("%#v\n", nc.Duration("howlong"))
+ fmt.Printf("%#v\n", nc.Float64("hay"))
+ fmt.Printf("%#v\n", nc.Generic("bloop"))
+ fmt.Printf("%#v\n", nc.Int64("bonk"))
+ fmt.Printf("%#v\n", nc.Int64Slice("burnks"))
+ fmt.Printf("%#v\n", nc.Int("bips"))
+ fmt.Printf("%#v\n", nc.IntSlice("blups"))
+ fmt.Printf("%#v\n", nc.String("snurt"))
+ fmt.Printf("%#v\n", nc.StringSlice("snurkles"))
+ fmt.Printf("%#v\n", nc.Uint("flub"))
+ fmt.Printf("%#v\n", nc.Uint64("florb"))
+ fmt.Printf("%#v\n", nc.GlobalBool("global-nope"))
+ fmt.Printf("%#v\n", nc.GlobalBoolT("global-nerp"))
+ fmt.Printf("%#v\n", nc.GlobalDuration("global-howlong"))
+ fmt.Printf("%#v\n", nc.GlobalFloat64("global-hay"))
+ fmt.Printf("%#v\n", nc.GlobalGeneric("global-bloop"))
+ fmt.Printf("%#v\n", nc.GlobalInt("global-bips"))
+ fmt.Printf("%#v\n", nc.GlobalIntSlice("global-blups"))
+ fmt.Printf("%#v\n", nc.GlobalString("global-snurt"))
+ fmt.Printf("%#v\n", nc.GlobalStringSlice("global-snurkles"))
+
+ fmt.Printf("%#v\n", nc.FlagNames())
+ fmt.Printf("%#v\n", nc.GlobalFlagNames())
+ fmt.Printf("%#v\n", nc.GlobalIsSet("wat"))
+ fmt.Printf("%#v\n", nc.GlobalSet("wat", "nope"))
+ fmt.Printf("%#v\n", nc.NArg())
+ fmt.Printf("%#v\n", nc.NumFlags())
+ fmt.Printf("%#v\n", nc.Parent())
+
+ nc.Set("wat", "also-nope")
+
+ ec := cli.NewExitError("ohwell", 86)
+ fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode())
+ fmt.Printf("made it!\n")
+ return ec
+ }
+
+ if os.Getenv("HEXY") != "" {
+ app.Writer = &hexWriter{}
+ app.ErrWriter = &hexWriter{}
+ }
+
+ app.Metadata = map[string]interface{}{
+ "layers": "many",
+ "explicable": false,
+ "whatever-values": 19.99,
+ }
+
+ app.Run(os.Args)
+}
+
+func wopAction(c *cli.Context) error {
+ fmt.Fprintf(c.App.Writer, ":wave: over here, eh\n")
+ return nil
+}
+```
+
## Contribution Guidelines
-Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch.
+Feel free to put up a pull request to fix a bug or maybe add a feature. I will
+give it a code review and make sure that it does not break backwards
+compatibility. If I or any other collaborators agree that it is in line with
+the vision of the project, we will work with you to get the code into
+a mergeable state and merge it into the master branch.
-If you have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together.
+If you have contributed something significant to the project, we will most
+likely add you as a collaborator. As a collaborator you are given the ability
+to merge others pull requests. It is very important that new code does not
+break existing code, so be careful about what code you do choose to merge.
-If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out.
+If you feel like you have contributed to the project but have not yet been
+added as a collaborator, we probably forgot to add you, please open an issue.
diff --git a/vendor/github.com/codegangsta/cli/altsrc/flag.go b/vendor/github.com/codegangsta/cli/altsrc/flag.go
index f13ffb4..3e44d02 100644
--- a/vendor/github.com/codegangsta/cli/altsrc/flag.go
+++ b/vendor/github.com/codegangsta/cli/altsrc/flag.go
@@ -7,7 +7,7 @@
"strconv"
"strings"
- "github.com/codegangsta/cli"
+ "github.com/urfave/cli"
)
// FlagInputSourceExtension is an extension interface of cli.Flag that
@@ -38,7 +38,7 @@
// InitInputSource is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
// input source based on the func provided. If there is no error it will then apply the new input source to any flags
// that are supported by the input source
-func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) func(context *cli.Context) error {
+func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc {
return func(context *cli.Context) error {
inputSource, err := createInputSource()
if err != nil {
@@ -52,7 +52,7 @@
// InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
// input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is
// no error it will then apply the new input source to any flags that are supported by the input source
-func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) func(context *cli.Context) error {
+func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) cli.BeforeFunc {
return func(context *cli.Context) error {
inputSource, err := createInputSource(context)
if err != nil {
diff --git a/vendor/github.com/codegangsta/cli/altsrc/flag_test.go b/vendor/github.com/codegangsta/cli/altsrc/flag_test.go
index ac4d1f5..218e9b8 100644
--- a/vendor/github.com/codegangsta/cli/altsrc/flag_test.go
+++ b/vendor/github.com/codegangsta/cli/altsrc/flag_test.go
@@ -8,7 +8,7 @@
"testing"
"time"
- "github.com/codegangsta/cli"
+ "github.com/urfave/cli"
)
type testApplyInputSource struct {
@@ -296,7 +296,7 @@
}
func runTest(t *testing.T, test testApplyInputSource) *cli.Context {
- inputSource := &MapInputSource{valueMap: map[string]interface{}{test.FlagName: test.MapValue}}
+ inputSource := &MapInputSource{valueMap: map[interface{}]interface{}{test.FlagName: test.MapValue}}
set := flag.NewFlagSet(test.FlagSetName, flag.ContinueOnError)
c := cli.NewContext(nil, set, nil)
if test.EnvVarName != "" && test.EnvVarValue != "" {
diff --git a/vendor/github.com/codegangsta/cli/altsrc/input_source_context.go b/vendor/github.com/codegangsta/cli/altsrc/input_source_context.go
index 6d695ff..8ea7e92 100644
--- a/vendor/github.com/codegangsta/cli/altsrc/input_source_context.go
+++ b/vendor/github.com/codegangsta/cli/altsrc/input_source_context.go
@@ -3,7 +3,7 @@
import (
"time"
- "github.com/codegangsta/cli"
+ "github.com/urfave/cli"
)
// InputSourceContext is an interface used to allow
diff --git a/vendor/github.com/codegangsta/cli/altsrc/map_input_source.go b/vendor/github.com/codegangsta/cli/altsrc/map_input_source.go
index f1670fb..b1c8e4f 100644
--- a/vendor/github.com/codegangsta/cli/altsrc/map_input_source.go
+++ b/vendor/github.com/codegangsta/cli/altsrc/map_input_source.go
@@ -3,15 +3,40 @@
import (
"fmt"
"reflect"
+ "strings"
"time"
- "github.com/codegangsta/cli"
+ "github.com/urfave/cli"
)
// MapInputSource implements InputSourceContext to return
// data from the map that is loaded.
type MapInputSource struct {
- valueMap map[string]interface{}
+ valueMap map[interface{}]interface{}
+}
+
+// nestedVal checks if the name has '.' delimiters.
+// If so, it tries to traverse the tree by the '.' delimited sections to find
+// a nested value for the key.
+func nestedVal(name string, tree map[interface{}]interface{}) (interface{}, bool) {
+ if sections := strings.Split(name, "."); len(sections) > 1 {
+ node := tree
+ for _, section := range sections[:len(sections)-1] {
+ if child, ok := node[section]; !ok {
+ return nil, false
+ } else {
+ if ctype, ok := child.(map[interface{}]interface{}); !ok {
+ return nil, false
+ } else {
+ node = ctype
+ }
+ }
+ }
+ if val, ok := node[sections[len(sections)-1]]; ok {
+ return val, true
+ }
+ }
+ return nil, false
}
// Int returns an int from the map if it exists otherwise returns 0
@@ -22,7 +47,14 @@
if !isType {
return 0, incorrectTypeForFlagError(name, "int", otherGenericValue)
}
-
+ return otherValue, nil
+ }
+ nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
+ if exists {
+ otherValue, isType := nestedGenericValue.(int)
+ if !isType {
+ return 0, incorrectTypeForFlagError(name, "int", nestedGenericValue)
+ }
return otherValue, nil
}
@@ -39,6 +71,14 @@
}
return otherValue, nil
}
+ nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
+ if exists {
+ otherValue, isType := nestedGenericValue.(time.Duration)
+ if !isType {
+ return 0, incorrectTypeForFlagError(name, "duration", nestedGenericValue)
+ }
+ return otherValue, nil
+ }
return 0, nil
}
@@ -53,6 +93,14 @@
}
return otherValue, nil
}
+ nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
+ if exists {
+ otherValue, isType := nestedGenericValue.(float64)
+ if !isType {
+ return 0, incorrectTypeForFlagError(name, "float64", nestedGenericValue)
+ }
+ return otherValue, nil
+ }
return 0, nil
}
@@ -67,6 +115,14 @@
}
return otherValue, nil
}
+ nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
+ if exists {
+ otherValue, isType := nestedGenericValue.(string)
+ if !isType {
+ return "", incorrectTypeForFlagError(name, "string", nestedGenericValue)
+ }
+ return otherValue, nil
+ }
return "", nil
}
@@ -81,6 +137,14 @@
}
return otherValue, nil
}
+ nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
+ if exists {
+ otherValue, isType := nestedGenericValue.([]string)
+ if !isType {
+ return nil, incorrectTypeForFlagError(name, "[]string", nestedGenericValue)
+ }
+ return otherValue, nil
+ }
return nil, nil
}
@@ -95,6 +159,14 @@
}
return otherValue, nil
}
+ nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
+ if exists {
+ otherValue, isType := nestedGenericValue.([]int)
+ if !isType {
+ return nil, incorrectTypeForFlagError(name, "[]int", nestedGenericValue)
+ }
+ return otherValue, nil
+ }
return nil, nil
}
@@ -109,6 +181,14 @@
}
return otherValue, nil
}
+ nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
+ if exists {
+ otherValue, isType := nestedGenericValue.(cli.Generic)
+ if !isType {
+ return nil, incorrectTypeForFlagError(name, "cli.Generic", nestedGenericValue)
+ }
+ return otherValue, nil
+ }
return nil, nil
}
@@ -123,6 +203,14 @@
}
return otherValue, nil
}
+ nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
+ if exists {
+ otherValue, isType := nestedGenericValue.(bool)
+ if !isType {
+ return false, incorrectTypeForFlagError(name, "bool", nestedGenericValue)
+ }
+ return otherValue, nil
+ }
return false, nil
}
@@ -137,6 +225,14 @@
}
return otherValue, nil
}
+ nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
+ if exists {
+ otherValue, isType := nestedGenericValue.(bool)
+ if !isType {
+ return true, incorrectTypeForFlagError(name, "bool", nestedGenericValue)
+ }
+ return otherValue, nil
+ }
return true, nil
}
diff --git a/vendor/github.com/codegangsta/cli/altsrc/yaml_command_test.go b/vendor/github.com/codegangsta/cli/altsrc/yaml_command_test.go
index 275bc64..31f78ce 100644
--- a/vendor/github.com/codegangsta/cli/altsrc/yaml_command_test.go
+++ b/vendor/github.com/codegangsta/cli/altsrc/yaml_command_test.go
@@ -11,7 +11,7 @@
"os"
"testing"
- "github.com/codegangsta/cli"
+ "github.com/urfave/cli"
)
func TestCommandYamlFileTest(t *testing.T) {
@@ -29,9 +29,10 @@
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
val := c.Int("test")
expect(t, val, 15)
+ return nil
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test"}),
@@ -61,9 +62,10 @@
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
val := c.Int("test")
expect(t, val, 10)
+ return nil
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}),
@@ -76,6 +78,41 @@
expect(t, err, nil)
}
+func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) {
+ app := cli.NewApp()
+ set := flag.NewFlagSet("test", 0)
+ ioutil.WriteFile("current.yaml", []byte(`top:
+ test: 15`), 0666)
+ defer os.Remove("current.yaml")
+
+ os.Setenv("THE_TEST", "10")
+ defer os.Setenv("THE_TEST", "")
+ test := []string{"test-cmd", "--load", "current.yaml"}
+ set.Parse(test)
+
+ c := cli.NewContext(app, set, nil)
+
+ command := &cli.Command{
+ Name: "test-cmd",
+ Aliases: []string{"tc"},
+ Usage: "this is for testing",
+ Description: "testing",
+ Action: func(c *cli.Context) error {
+ val := c.Int("top.test")
+ expect(t, val, 10)
+ return nil
+ },
+ Flags: []cli.Flag{
+ NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}),
+ cli.StringFlag{Name: "load"}},
+ }
+ command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
+
+ err := command.Run(c)
+
+ expect(t, err, nil)
+}
+
func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
@@ -92,9 +129,10 @@
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
val := c.Int("test")
expect(t, val, 7)
+ return nil
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test"}),
@@ -107,6 +145,39 @@
expect(t, err, nil)
}
+func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) {
+ app := cli.NewApp()
+ set := flag.NewFlagSet("test", 0)
+ ioutil.WriteFile("current.yaml", []byte(`top:
+ test: 15`), 0666)
+ defer os.Remove("current.yaml")
+
+ test := []string{"test-cmd", "--load", "current.yaml", "--top.test", "7"}
+ set.Parse(test)
+
+ c := cli.NewContext(app, set, nil)
+
+ command := &cli.Command{
+ Name: "test-cmd",
+ Aliases: []string{"tc"},
+ Usage: "this is for testing",
+ Description: "testing",
+ Action: func(c *cli.Context) error {
+ val := c.Int("top.test")
+ expect(t, val, 7)
+ return nil
+ },
+ Flags: []cli.Flag{
+ NewIntFlag(cli.IntFlag{Name: "top.test"}),
+ cli.StringFlag{Name: "load"}},
+ }
+ command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
+
+ err := command.Run(c)
+
+ expect(t, err, nil)
+}
+
func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
@@ -123,9 +194,10 @@
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
val := c.Int("test")
expect(t, val, 15)
+ return nil
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test", Value: 7}),
@@ -138,6 +210,39 @@
expect(t, err, nil)
}
+func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) {
+ app := cli.NewApp()
+ set := flag.NewFlagSet("test", 0)
+ ioutil.WriteFile("current.yaml", []byte(`top:
+ test: 15`), 0666)
+ defer os.Remove("current.yaml")
+
+ test := []string{"test-cmd", "--load", "current.yaml"}
+ set.Parse(test)
+
+ c := cli.NewContext(app, set, nil)
+
+ command := &cli.Command{
+ Name: "test-cmd",
+ Aliases: []string{"tc"},
+ Usage: "this is for testing",
+ Description: "testing",
+ Action: func(c *cli.Context) error {
+ val := c.Int("top.test")
+ expect(t, val, 15)
+ return nil
+ },
+ Flags: []cli.Flag{
+ NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}),
+ cli.StringFlag{Name: "load"}},
+ }
+ command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
+
+ err := command.Run(c)
+
+ expect(t, err, nil)
+}
+
func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
@@ -157,9 +262,10 @@
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
- Action: func(c *cli.Context) {
+ Action: func(c *cli.Context) error {
val := c.Int("test")
expect(t, val, 11)
+ return nil
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}),
@@ -170,3 +276,38 @@
expect(t, err, nil)
}
+
+func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *testing.T) {
+ app := cli.NewApp()
+ set := flag.NewFlagSet("test", 0)
+ ioutil.WriteFile("current.yaml", []byte(`top:
+ test: 15`), 0666)
+ defer os.Remove("current.yaml")
+
+ os.Setenv("THE_TEST", "11")
+ defer os.Setenv("THE_TEST", "")
+
+ test := []string{"test-cmd", "--load", "current.yaml"}
+ set.Parse(test)
+
+ c := cli.NewContext(app, set, nil)
+
+ command := &cli.Command{
+ Name: "test-cmd",
+ Aliases: []string{"tc"},
+ Usage: "this is for testing",
+ Description: "testing",
+ Action: func(c *cli.Context) error {
+ val := c.Int("top.test")
+ expect(t, val, 11)
+ return nil
+ },
+ Flags: []cli.Flag{
+ NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}),
+ cli.StringFlag{Name: "load"}},
+ }
+ command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
+ err := command.Run(c)
+
+ expect(t, err, nil)
+}
diff --git a/vendor/github.com/codegangsta/cli/altsrc/yaml_file_loader.go b/vendor/github.com/codegangsta/cli/altsrc/yaml_file_loader.go
index 4fb0965..b4e3365 100644
--- a/vendor/github.com/codegangsta/cli/altsrc/yaml_file_loader.go
+++ b/vendor/github.com/codegangsta/cli/altsrc/yaml_file_loader.go
@@ -12,7 +12,7 @@
"net/url"
"os"
- "github.com/codegangsta/cli"
+ "github.com/urfave/cli"
"gopkg.in/yaml.v2"
)
@@ -24,7 +24,7 @@
// NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath.
func NewYamlSourceFromFile(file string) (InputSourceContext, error) {
ysc := &yamlSourceContext{FilePath: file}
- var results map[string]interface{}
+ var results map[interface{}]interface{}
err := readCommandYaml(ysc.FilePath, &results)
if err != nil {
return nil, fmt.Errorf("Unable to load Yaml file '%s': inner error: \n'%v'", ysc.FilePath, err.Error())
diff --git a/vendor/github.com/codegangsta/cli/app.go b/vendor/github.com/codegangsta/cli/app.go
index bd20a2d..a046c01 100644
--- a/vendor/github.com/codegangsta/cli/app.go
+++ b/vendor/github.com/codegangsta/cli/app.go
@@ -6,10 +6,27 @@
"io/ioutil"
"os"
"path/filepath"
+ "reflect"
"sort"
+ "strings"
"time"
)
+var (
+ changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
+ appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
+ runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
+
+ contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
+
+ errNonFuncAction = NewExitError("ERROR invalid Action type. "+
+ fmt.Sprintf("Must be a func of type `cli.ActionFunc`. %s", contactSysadmin)+
+ fmt.Sprintf("See %s", appActionDeprecationURL), 2)
+ errInvalidActionSignature = NewExitError("ERROR invalid Action signature. "+
+ fmt.Sprintf("Must be `cli.ActionFunc`. %s", contactSysadmin)+
+ fmt.Sprintf("See %s", appActionDeprecationURL), 2)
+)
+
// App is the main structure of a cli application. It is recommended that
// an app be created with the cli.NewApp() function
type App struct {
@@ -35,24 +52,25 @@
HideHelp bool
// Boolean to hide built-in version flag and the VERSION section of help
HideVersion bool
- // Populate on app startup, only gettable throught method Categories()
+ // Populate on app startup, only gettable through method Categories()
categories CommandCategories
// An action to execute when the bash-completion flag is set
- BashComplete func(context *Context)
+ BashComplete BashCompleteFunc
// An action to execute before any subcommands are run, but after the context is ready
// If a non-nil error is returned, no subcommands are run
- Before func(context *Context) error
+ Before BeforeFunc
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
- After func(context *Context) error
+ After AfterFunc
// The action to execute when no subcommands are specified
- Action func(context *Context)
+ Action interface{}
+ // TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
+ // of deprecation period has passed, maybe?
+
// Execute this function if the proper command cannot be found
- CommandNotFound func(context *Context, command string)
- // Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
- // This function is able to replace the original error messages.
- // If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
- OnUsageError func(context *Context, err error, isSubcommand bool) error
+ CommandNotFound CommandNotFoundFunc
+ // Execute this function if an usage error occurs
+ OnUsageError OnUsageErrorFunc
// Compilation date
Compiled time.Time
// List of all authors who contributed
@@ -65,6 +83,12 @@
Email string
// Writer writer to write output to
Writer io.Writer
+ // ErrWriter writes error output
+ ErrWriter io.Writer
+ // Other custom info
+ Metadata map[string]interface{}
+
+ didSetup bool
}
// Tries to find out when this binary was compiled.
@@ -77,7 +101,8 @@
return info.ModTime()
}
-// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
+// NewApp creates a new cli Application with some reasonable defaults for Name,
+// Usage, Version and Action.
func NewApp() *App {
return &App{
Name: filepath.Base(os.Args[0]),
@@ -92,8 +117,16 @@
}
}
-// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
-func (a *App) Run(arguments []string) (err error) {
+// Setup runs initialization code to ensure all data structures are ready for
+// `Run` or inspection prior to `Run`. It is internally called by `Run`, but
+// will return early if setup has already happened.
+func (a *App) Setup() {
+ if a.didSetup {
+ return
+ }
+
+ a.didSetup = true
+
if a.Author != "" || a.Email != "" {
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
}
@@ -107,13 +140,6 @@
}
a.Commands = newCmds
- a.categories = CommandCategories{}
- for _, command := range a.Commands {
- a.categories = a.categories.AddCommand(command.Category, command)
- }
- sort.Sort(a.categories)
-
- // append help to commands
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
a.Commands = append(a.Commands, helpCommand)
if (HelpFlag != BoolFlag{}) {
@@ -121,7 +147,6 @@
}
}
- //append version/help flags
if a.EnableBashCompletion {
a.appendFlag(BashCompletionFlag)
}
@@ -130,6 +155,18 @@
a.appendFlag(VersionFlag)
}
+ a.categories = CommandCategories{}
+ for _, command := range a.Commands {
+ a.categories = a.categories.AddCommand(command.Category, command)
+ }
+ sort.Sort(a.categories)
+}
+
+// Run is the entry point to the cli app. Parses the arguments slice and routes
+// to the proper flag/args combination
+func (a *App) Run(arguments []string) (err error) {
+ a.Setup()
+
// parse flags
set := flagSet(a.Name, a.Flags)
set.SetOutput(ioutil.Discard)
@@ -149,12 +186,12 @@
if err != nil {
if a.OnUsageError != nil {
err := a.OnUsageError(context, err, false)
- return err
- } else {
- fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
- ShowAppHelp(context)
+ HandleExitCoder(err)
return err
}
+ fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
+ ShowAppHelp(context)
+ return err
}
if !a.HideHelp && checkHelp(context) {
@@ -180,10 +217,12 @@
}
if a.Before != nil {
- err = a.Before(context)
- if err != nil {
- fmt.Fprintf(a.Writer, "%v\n\n", err)
+ beforeErr := a.Before(context)
+ if beforeErr != nil {
+ fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
ShowAppHelp(context)
+ HandleExitCoder(beforeErr)
+ err = beforeErr
return err
}
}
@@ -198,19 +237,25 @@
}
// Run default Action
- a.Action(context)
- return nil
+ err = HandleAction(a.Action, context)
+
+ HandleExitCoder(err)
+ return err
}
-// Another entry point to the cli app, takes care of passing arguments and error handling
+// DEPRECATED: Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() {
+ fmt.Fprintf(a.errWriter(),
+ "DEPRECATED cli.App.RunAndExitOnError. %s See %s\n",
+ contactSysadmin, runAndExitOnErrorDeprecationURL)
if err := a.Run(os.Args); err != nil {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
+ fmt.Fprintln(a.errWriter(), err)
+ OsExiter(1)
}
}
-// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
+// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
+// generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
// append help to commands
if len(a.Commands) > 0 {
@@ -261,12 +306,12 @@
if err != nil {
if a.OnUsageError != nil {
err = a.OnUsageError(context, err, true)
- return err
- } else {
- fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
- ShowSubcommandHelp(context)
+ HandleExitCoder(err)
return err
}
+ fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
+ ShowSubcommandHelp(context)
+ return err
}
if len(a.Commands) > 0 {
@@ -283,6 +328,7 @@
defer func() {
afterErr := a.After(context)
if afterErr != nil {
+ HandleExitCoder(err)
if err != nil {
err = NewMultiError(err, afterErr)
} else {
@@ -293,8 +339,10 @@
}
if a.Before != nil {
- err := a.Before(context)
- if err != nil {
+ beforeErr := a.Before(context)
+ if beforeErr != nil {
+ HandleExitCoder(beforeErr)
+ err = beforeErr
return err
}
}
@@ -309,12 +357,13 @@
}
// Run default Action
- a.Action(context)
+ err = HandleAction(a.Action, context)
- return nil
+ HandleExitCoder(err)
+ return err
}
-// Returns the named command on App. Returns nil if the command does not exist
+// Command returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
for _, c := range a.Commands {
if c.HasName(name) {
@@ -325,11 +374,46 @@
return nil
}
-// Returnes the array containing all the categories with the commands they contain
+// Categories returns a slice containing all the categories with the commands they contain
func (a *App) Categories() CommandCategories {
return a.categories
}
+// VisibleCategories returns a slice of categories and commands that are
+// Hidden=false
+func (a *App) VisibleCategories() []*CommandCategory {
+ ret := []*CommandCategory{}
+ for _, category := range a.categories {
+ if visible := func() *CommandCategory {
+ for _, command := range category.Commands {
+ if !command.Hidden {
+ return category
+ }
+ }
+ return nil
+ }(); visible != nil {
+ ret = append(ret, visible)
+ }
+ }
+ return ret
+}
+
+// VisibleCommands returns a slice of the Commands with Hidden=false
+func (a *App) VisibleCommands() []Command {
+ ret := []Command{}
+ for _, command := range a.Commands {
+ if !command.Hidden {
+ ret = append(ret, command)
+ }
+ }
+ return ret
+}
+
+// VisibleFlags returns a slice of the Flags with Hidden=false
+func (a *App) VisibleFlags() []Flag {
+ return visibleFlags(a.Flags)
+}
+
func (a *App) hasFlag(flag Flag) bool {
for _, f := range a.Flags {
if flag == f {
@@ -340,6 +424,16 @@
return false
}
+func (a *App) errWriter() io.Writer {
+
+ // When the app ErrWriter is nil use the package level one.
+ if a.ErrWriter == nil {
+ return ErrWriter
+ }
+
+ return a.ErrWriter
+}
+
func (a *App) appendFlag(flag Flag) {
if !a.hasFlag(flag) {
a.Flags = append(a.Flags, flag)
@@ -361,3 +455,45 @@
return fmt.Sprintf("%v %v", a.Name, e)
}
+
+// HandleAction uses ✧✧✧reflection✧✧✧ to figure out if the given Action is an
+// ActionFunc, a func with the legacy signature for Action, or some other
+// invalid thing. If it's an ActionFunc or a func with the legacy signature for
+// Action, the func is run!
+func HandleAction(action interface{}, context *Context) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ // Try to detect a known reflection error from *this scope*, rather than
+ // swallowing all panics that may happen when calling an Action func.
+ s := fmt.Sprintf("%v", r)
+ if strings.HasPrefix(s, "reflect: ") && strings.Contains(s, "too many input arguments") {
+ err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v. See %s", r, appActionDeprecationURL), 2)
+ } else {
+ panic(r)
+ }
+ }
+ }()
+
+ if reflect.TypeOf(action).Kind() != reflect.Func {
+ return errNonFuncAction
+ }
+
+ vals := reflect.ValueOf(action).Call([]reflect.Value{reflect.ValueOf(context)})
+
+ if len(vals) == 0 {
+ fmt.Fprintf(ErrWriter,
+ "DEPRECATED Action signature. Must be `cli.ActionFunc`. %s See %s\n",
+ contactSysadmin, appActionDeprecationURL)
+ return nil
+ }
+
+ if len(vals) > 1 {
+ return errInvalidActionSignature
+ }
+
+ if retErr, ok := vals[0].Interface().(error); vals[0].IsValid() && ok {
+ return retErr
+ }
+
+ return err
+}
diff --git a/vendor/github.com/codegangsta/cli/app_test.go b/vendor/github.com/codegangsta/cli/app_test.go
index ebf26c7..9c6b960 100644
--- a/vendor/github.com/codegangsta/cli/app_test.go
+++ b/vendor/github.com/codegangsta/cli/app_test.go
@@ -13,6 +13,10 @@
"testing"
)
+type opCounts struct {
+ Total, BashComplete, OnUsageError, Before, CommandNotFound, Action, After, SubCommand int
+}
+
func ExampleApp_Run() {
// set args for examples sake
os.Args = []string{"greet", "--name", "Jeremy"}
@@ -22,13 +26,14 @@
app.Flags = []Flag{
StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
}
- app.Action = func(c *Context) {
+ app.Action = func(c *Context) error {
fmt.Printf("Hello %v\n", c.String("name"))
+ return nil
}
app.UsageText = "app [first_arg] [second_arg]"
app.Author = "Harrison"
app.Email = "harrison@lolwut.com"
- app.Authors = []Author{Author{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
+ app.Authors = []Author{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
app.Run(os.Args)
// Output:
// Hello Jeremy
@@ -58,8 +63,9 @@
Usage: "Name of the person to greet",
},
},
- Action: func(c *Context) {
+ Action: func(c *Context) error {
fmt.Println("Hello,", c.String("name"))
+ return nil
},
},
},
@@ -86,8 +92,9 @@
Aliases: []string{"d"},
Usage: "use it to see a description",
Description: "This is how we describe describeit the function",
- Action: func(c *Context) {
+ Action: func(c *Context) error {
fmt.Printf("i like to describe things")
+ return nil
},
},
}
@@ -116,15 +123,17 @@
Aliases: []string{"d"},
Usage: "use it to see a description",
Description: "This is how we describe describeit the function",
- Action: func(c *Context) {
+ Action: func(c *Context) error {
fmt.Printf("i like to describe things")
+ return nil
},
}, {
Name: "next",
Usage: "next example",
Description: "more stuff to see when generating bash completion",
- Action: func(c *Context) {
+ Action: func(c *Context) error {
fmt.Printf("the next example")
+ return nil
},
},
}
@@ -142,8 +151,9 @@
s := ""
app := NewApp()
- app.Action = func(c *Context) {
+ app.Action = func(c *Context) error {
s = s + c.Args().First()
+ return nil
}
err := app.Run([]string{"command", "foo"})
@@ -188,9 +198,10 @@
Flags: []Flag{
StringFlag{Name: "option", Value: "", Usage: "some option"},
},
- Action: func(c *Context) {
+ Action: func(c *Context) error {
parsedOption = c.String("option")
firstArg = c.Args().First()
+ return nil
},
}
app.Commands = []Command{command}
@@ -208,8 +219,9 @@
a.Commands = []Command{
{
Name: "foo",
- Action: func(c *Context) {
+ Action: func(c *Context) error {
context = c
+ return nil
},
Flags: []Flag{
StringFlag{
@@ -237,9 +249,10 @@
Flags: []Flag{
StringFlag{Name: "option", Value: "", Usage: "some option"},
},
- Action: func(c *Context) {
+ Action: func(c *Context) error {
parsedOption = c.String("option")
args = c.Args()
+ return nil
},
}
app.Commands = []Command{command}
@@ -258,8 +271,9 @@
app := NewApp()
command := Command{
Name: "cmd",
- Action: func(c *Context) {
+ Action: func(c *Context) error {
args = c.Args()
+ return nil
},
}
app.Commands = []Command{command}
@@ -276,8 +290,9 @@
app := NewApp()
command := Command{
Name: "cmd",
- Action: func(c *Context) {
+ Action: func(c *Context) error {
args = c.Args()
+ return nil
},
}
app.Commands = []Command{command}
@@ -289,6 +304,48 @@
expect(t, args[2], "notAFlagAtAll")
}
+func TestApp_VisibleCommands(t *testing.T) {
+ app := NewApp()
+ app.Commands = []Command{
+ {
+ Name: "frob",
+ HelpName: "foo frob",
+ Action: func(_ *Context) error { return nil },
+ },
+ {
+ Name: "frib",
+ HelpName: "foo frib",
+ Hidden: true,
+ Action: func(_ *Context) error { return nil },
+ },
+ }
+
+ app.Setup()
+ expected := []Command{
+ app.Commands[0],
+ app.Commands[2], // help
+ }
+ actual := app.VisibleCommands()
+ expect(t, len(expected), len(actual))
+ for i, actualCommand := range actual {
+ expectedCommand := expected[i]
+
+ if expectedCommand.Action != nil {
+ // comparing func addresses is OK!
+ expect(t, fmt.Sprintf("%p", expectedCommand.Action), fmt.Sprintf("%p", actualCommand.Action))
+ }
+
+ // nil out funcs, as they cannot be compared
+ // (https://github.com/golang/go/issues/8554)
+ expectedCommand.Action = nil
+ actualCommand.Action = nil
+
+ if !reflect.DeepEqual(expectedCommand, actualCommand) {
+ t.Errorf("expected\n%#v\n!=\n%#v", expectedCommand, actualCommand)
+ }
+ }
+}
+
func TestApp_Float64Flag(t *testing.T) {
var meters float64
@@ -296,8 +353,9 @@
app.Flags = []Flag{
Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"},
}
- app.Action = func(c *Context) {
+ app.Action = func(c *Context) error {
meters = c.Float64("height")
+ return nil
}
app.Run([]string{"", "--height", "1.93"})
@@ -316,11 +374,12 @@
IntSliceFlag{Name: "p", Value: &IntSlice{}, Usage: "set one or more ip addr"},
StringSliceFlag{Name: "ip", Value: &StringSlice{}, Usage: "set one or more ports to open"},
},
- Action: func(c *Context) {
+ Action: func(c *Context) error {
parsedIntSlice = c.IntSlice("p")
parsedStringSlice = c.StringSlice("ip")
parsedOption = c.String("option")
firstArg = c.Args().First()
+ return nil
},
}
app.Commands = []Command{command}
@@ -373,9 +432,10 @@
IntSliceFlag{Name: "a", Usage: "set numbers"},
StringSliceFlag{Name: "str", Usage: "set strings"},
},
- Action: func(c *Context) {
+ Action: func(c *Context) error {
parsedIntSlice = c.IntSlice("a")
parsedStringSlice = c.StringSlice("str")
+ return nil
},
}
app.Commands = []Command{command}
@@ -439,14 +499,15 @@
}
func TestApp_BeforeFunc(t *testing.T) {
- beforeRun, subcommandRun := false, false
+ counts := &opCounts{}
beforeError := fmt.Errorf("fail")
var err error
app := NewApp()
app.Before = func(c *Context) error {
- beforeRun = true
+ counts.Total++
+ counts.Before = counts.Total
s := c.String("opt")
if s == "fail" {
return beforeError
@@ -456,10 +517,12 @@
}
app.Commands = []Command{
- Command{
+ {
Name: "sub",
- Action: func(c *Context) {
- subcommandRun = true
+ Action: func(c *Context) error {
+ counts.Total++
+ counts.SubCommand = counts.Total
+ return nil
},
},
}
@@ -475,16 +538,16 @@
t.Fatalf("Run error: %s", err)
}
- if beforeRun == false {
+ if counts.Before != 1 {
t.Errorf("Before() not executed when expected")
}
- if subcommandRun == false {
+ if counts.SubCommand != 2 {
t.Errorf("Subcommand not executed when expected")
}
// reset
- beforeRun, subcommandRun = false, false
+ counts = &opCounts{}
// run with the Before() func failing
err = app.Run([]string{"command", "--opt", "fail", "sub"})
@@ -494,25 +557,49 @@
t.Errorf("Run error expected, but not received")
}
- if beforeRun == false {
+ if counts.Before != 1 {
t.Errorf("Before() not executed when expected")
}
- if subcommandRun == true {
+ if counts.SubCommand != 0 {
t.Errorf("Subcommand executed when NOT expected")
}
+ // reset
+ counts = &opCounts{}
+
+ afterError := errors.New("fail again")
+ app.After = func(_ *Context) error {
+ return afterError
+ }
+
+ // run with the Before() func failing, wrapped by After()
+ err = app.Run([]string{"command", "--opt", "fail", "sub"})
+
+ // should be the same error produced by the Before func
+ if _, ok := err.(MultiError); !ok {
+ t.Errorf("MultiError expected, but not received")
+ }
+
+ if counts.Before != 1 {
+ t.Errorf("Before() not executed when expected")
+ }
+
+ if counts.SubCommand != 0 {
+ t.Errorf("Subcommand executed when NOT expected")
+ }
}
func TestApp_AfterFunc(t *testing.T) {
- afterRun, subcommandRun := false, false
+ counts := &opCounts{}
afterError := fmt.Errorf("fail")
var err error
app := NewApp()
app.After = func(c *Context) error {
- afterRun = true
+ counts.Total++
+ counts.After = counts.Total
s := c.String("opt")
if s == "fail" {
return afterError
@@ -522,10 +609,12 @@
}
app.Commands = []Command{
- Command{
+ {
Name: "sub",
- Action: func(c *Context) {
- subcommandRun = true
+ Action: func(c *Context) error {
+ counts.Total++
+ counts.SubCommand = counts.Total
+ return nil
},
},
}
@@ -541,16 +630,16 @@
t.Fatalf("Run error: %s", err)
}
- if afterRun == false {
+ if counts.After != 2 {
t.Errorf("After() not executed when expected")
}
- if subcommandRun == false {
+ if counts.SubCommand != 1 {
t.Errorf("Subcommand not executed when expected")
}
// reset
- afterRun, subcommandRun = false, false
+ counts = &opCounts{}
// run with the Before() func failing
err = app.Run([]string{"command", "--opt", "fail", "sub"})
@@ -560,11 +649,11 @@
t.Errorf("Run error expected, but not received")
}
- if afterRun == false {
+ if counts.After != 2 {
t.Errorf("After() not executed when expected")
}
- if subcommandRun == false {
+ if counts.SubCommand != 1 {
t.Errorf("Subcommand not executed when expected")
}
}
@@ -605,7 +694,7 @@
}
}
-func TestAppVersionPrinter(t *testing.T) {
+func TestApp_VersionPrinter(t *testing.T) {
oldPrinter := VersionPrinter
defer func() {
VersionPrinter = oldPrinter
@@ -625,81 +714,172 @@
}
}
-func TestAppCommandNotFound(t *testing.T) {
- beforeRun, subcommandRun := false, false
+func TestApp_CommandNotFound(t *testing.T) {
+ counts := &opCounts{}
app := NewApp()
app.CommandNotFound = func(c *Context, command string) {
- beforeRun = true
+ counts.Total++
+ counts.CommandNotFound = counts.Total
}
app.Commands = []Command{
- Command{
+ {
Name: "bar",
- Action: func(c *Context) {
- subcommandRun = true
+ Action: func(c *Context) error {
+ counts.Total++
+ counts.SubCommand = counts.Total
+ return nil
},
},
}
app.Run([]string{"command", "foo"})
- expect(t, beforeRun, true)
- expect(t, subcommandRun, false)
+ expect(t, counts.CommandNotFound, 1)
+ expect(t, counts.SubCommand, 0)
+ expect(t, counts.Total, 1)
}
-func TestGlobalFlag(t *testing.T) {
- var globalFlag string
- var globalFlagSet bool
+func TestApp_OrderOfOperations(t *testing.T) {
+ counts := &opCounts{}
+
+ resetCounts := func() { counts = &opCounts{} }
+
app := NewApp()
- app.Flags = []Flag{
- StringFlag{Name: "global, g", Usage: "global"},
- }
- app.Action = func(c *Context) {
- globalFlag = c.GlobalString("global")
- globalFlagSet = c.GlobalIsSet("global")
- }
- app.Run([]string{"command", "-g", "foo"})
- expect(t, globalFlag, "foo")
- expect(t, globalFlagSet, true)
-
-}
-
-func TestGlobalFlagsInSubcommands(t *testing.T) {
- subcommandRun := false
- parentFlag := false
- app := NewApp()
-
- app.Flags = []Flag{
- BoolFlag{Name: "debug, d", Usage: "Enable debugging"},
+ app.EnableBashCompletion = true
+ app.BashComplete = func(c *Context) {
+ counts.Total++
+ counts.BashComplete = counts.Total
}
+ app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
+ counts.Total++
+ counts.OnUsageError = counts.Total
+ return errors.New("hay OnUsageError")
+ }
+
+ beforeNoError := func(c *Context) error {
+ counts.Total++
+ counts.Before = counts.Total
+ return nil
+ }
+
+ beforeError := func(c *Context) error {
+ counts.Total++
+ counts.Before = counts.Total
+ return errors.New("hay Before")
+ }
+
+ app.Before = beforeNoError
+ app.CommandNotFound = func(c *Context, command string) {
+ counts.Total++
+ counts.CommandNotFound = counts.Total
+ }
+
+ afterNoError := func(c *Context) error {
+ counts.Total++
+ counts.After = counts.Total
+ return nil
+ }
+
+ afterError := func(c *Context) error {
+ counts.Total++
+ counts.After = counts.Total
+ return errors.New("hay After")
+ }
+
+ app.After = afterNoError
app.Commands = []Command{
- Command{
- Name: "foo",
- Flags: []Flag{
- BoolFlag{Name: "parent, p", Usage: "Parent flag"},
- },
- Subcommands: []Command{
- {
- Name: "bar",
- Action: func(c *Context) {
- if c.GlobalBool("debug") {
- subcommandRun = true
- }
- if c.GlobalBool("parent") {
- parentFlag = true
- }
- },
- },
+ {
+ Name: "bar",
+ Action: func(c *Context) error {
+ counts.Total++
+ counts.SubCommand = counts.Total
+ return nil
},
},
}
- app.Run([]string{"command", "-d", "foo", "-p", "bar"})
+ app.Action = func(c *Context) error {
+ counts.Total++
+ counts.Action = counts.Total
+ return nil
+ }
- expect(t, subcommandRun, true)
- expect(t, parentFlag, true)
+ _ = app.Run([]string{"command", "--nope"})
+ expect(t, counts.OnUsageError, 1)
+ expect(t, counts.Total, 1)
+
+ resetCounts()
+
+ _ = app.Run([]string{"command", "--generate-bash-completion"})
+ expect(t, counts.BashComplete, 1)
+ expect(t, counts.Total, 1)
+
+ resetCounts()
+
+ oldOnUsageError := app.OnUsageError
+ app.OnUsageError = nil
+ _ = app.Run([]string{"command", "--nope"})
+ expect(t, counts.Total, 0)
+ app.OnUsageError = oldOnUsageError
+
+ resetCounts()
+
+ _ = app.Run([]string{"command", "foo"})
+ expect(t, counts.OnUsageError, 0)
+ expect(t, counts.Before, 1)
+ expect(t, counts.CommandNotFound, 0)
+ expect(t, counts.Action, 2)
+ expect(t, counts.After, 3)
+ expect(t, counts.Total, 3)
+
+ resetCounts()
+
+ app.Before = beforeError
+ _ = app.Run([]string{"command", "bar"})
+ expect(t, counts.OnUsageError, 0)
+ expect(t, counts.Before, 1)
+ expect(t, counts.After, 2)
+ expect(t, counts.Total, 2)
+ app.Before = beforeNoError
+
+ resetCounts()
+
+ app.After = nil
+ _ = app.Run([]string{"command", "bar"})
+ expect(t, counts.OnUsageError, 0)
+ expect(t, counts.Before, 1)
+ expect(t, counts.SubCommand, 2)
+ expect(t, counts.Total, 2)
+ app.After = afterNoError
+
+ resetCounts()
+
+ app.After = afterError
+ err := app.Run([]string{"command", "bar"})
+ if err == nil {
+ t.Fatalf("expected a non-nil error")
+ }
+ expect(t, counts.OnUsageError, 0)
+ expect(t, counts.Before, 1)
+ expect(t, counts.SubCommand, 2)
+ expect(t, counts.After, 3)
+ expect(t, counts.Total, 3)
+ app.After = afterNoError
+
+ resetCounts()
+
+ oldCommands := app.Commands
+ app.Commands = nil
+ _ = app.Run([]string{"command"})
+ expect(t, counts.OnUsageError, 0)
+ expect(t, counts.Before, 1)
+ expect(t, counts.Action, 2)
+ expect(t, counts.After, 3)
+ expect(t, counts.Total, 3)
+ app.Commands = oldCommands
}
func TestApp_Run_CommandWithSubcommandHasHelpTopic(t *testing.T) {
@@ -891,8 +1071,9 @@
app.Name = "boom"
app.Usage = "make an explosive entrance"
app.Writer = buf
- app.Action = func(c *Context) {
+ app.Action = func(c *Context) error {
buf.WriteString("boom I say!")
+ return nil
}
err := app.Run(args)
@@ -922,8 +1103,9 @@
app.Usage = "make an explosive entrance"
app.Version = "0.1.0"
app.Writer = buf
- app.Action = func(c *Context) {
+ app.Action = func(c *Context) error {
buf.WriteString("boom I say!")
+ return nil
}
err := app.Run(args)
@@ -943,16 +1125,17 @@
func TestApp_Run_Categories(t *testing.T) {
app := NewApp()
app.Name = "categories"
+ app.HideHelp = true
app.Commands = []Command{
- Command{
+ {
Name: "command1",
Category: "1",
},
- Command{
+ {
Name: "command2",
Category: "1",
},
- Command{
+ {
Name: "command3",
Category: "2",
},
@@ -984,14 +1167,120 @@
output := buf.String()
t.Logf("output: %q\n", buf.Bytes())
- if !strings.Contains(output, "1:\n command1") {
- t.Errorf("want buffer to include category %q, did not: \n%q", "1:\n command1", output)
+ if !strings.Contains(output, "1:\n command1") {
+ t.Errorf("want buffer to include category %q, did not: \n%q", "1:\n command1", output)
}
}
+func TestApp_VisibleCategories(t *testing.T) {
+ app := NewApp()
+ app.Name = "visible-categories"
+ app.HideHelp = true
+ app.Commands = []Command{
+ {
+ Name: "command1",
+ Category: "1",
+ HelpName: "foo command1",
+ Hidden: true,
+ },
+ {
+ Name: "command2",
+ Category: "2",
+ HelpName: "foo command2",
+ },
+ {
+ Name: "command3",
+ Category: "3",
+ HelpName: "foo command3",
+ },
+ }
+
+ expected := []*CommandCategory{
+ {
+ Name: "2",
+ Commands: []Command{
+ app.Commands[1],
+ },
+ },
+ {
+ Name: "3",
+ Commands: []Command{
+ app.Commands[2],
+ },
+ },
+ }
+
+ app.Setup()
+ expect(t, expected, app.VisibleCategories())
+
+ app = NewApp()
+ app.Name = "visible-categories"
+ app.HideHelp = true
+ app.Commands = []Command{
+ {
+ Name: "command1",
+ Category: "1",
+ HelpName: "foo command1",
+ Hidden: true,
+ },
+ {
+ Name: "command2",
+ Category: "2",
+ HelpName: "foo command2",
+ Hidden: true,
+ },
+ {
+ Name: "command3",
+ Category: "3",
+ HelpName: "foo command3",
+ },
+ }
+
+ expected = []*CommandCategory{
+ {
+ Name: "3",
+ Commands: []Command{
+ app.Commands[2],
+ },
+ },
+ }
+
+ app.Setup()
+ expect(t, expected, app.VisibleCategories())
+
+ app = NewApp()
+ app.Name = "visible-categories"
+ app.HideHelp = true
+ app.Commands = []Command{
+ {
+ Name: "command1",
+ Category: "1",
+ HelpName: "foo command1",
+ Hidden: true,
+ },
+ {
+ Name: "command2",
+ Category: "2",
+ HelpName: "foo command2",
+ Hidden: true,
+ },
+ {
+ Name: "command3",
+ Category: "3",
+ HelpName: "foo command3",
+ Hidden: true,
+ },
+ }
+
+ expected = []*CommandCategory{}
+
+ app.Setup()
+ expect(t, expected, app.VisibleCategories())
+}
+
func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
app := NewApp()
- app.Action = func(c *Context) {}
+ app.Action = func(c *Context) error { return nil }
app.Before = func(c *Context) error { return fmt.Errorf("before error") }
app.After = func(c *Context) error { return fmt.Errorf("after error") }
@@ -1011,9 +1300,9 @@
func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
app := NewApp()
app.Commands = []Command{
- Command{
+ {
Subcommands: []Command{
- Command{
+ {
Name: "sub",
},
},
@@ -1051,7 +1340,7 @@
return errors.New("intercepted: " + err.Error())
}
app.Commands = []Command{
- Command{
+ {
Name: "bar",
},
}
@@ -1081,7 +1370,7 @@
return errors.New("intercepted: " + err.Error())
}
app.Commands = []Command{
- Command{
+ {
Name: "bar",
},
}
@@ -1095,3 +1384,88 @@
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
}
}
+
+func TestHandleAction_WithNonFuncAction(t *testing.T) {
+ app := NewApp()
+ app.Action = 42
+ err := HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
+
+ if err == nil {
+ t.Fatalf("expected to receive error from Run, got none")
+ }
+
+ exitErr, ok := err.(*ExitError)
+
+ if !ok {
+ t.Fatalf("expected to receive a *ExitError")
+ }
+
+ if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type") {
+ t.Fatalf("expected an unknown Action error, but got: %v", exitErr.Error())
+ }
+
+ if exitErr.ExitCode() != 2 {
+ t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode())
+ }
+}
+
+func TestHandleAction_WithInvalidFuncSignature(t *testing.T) {
+ app := NewApp()
+ app.Action = func() string { return "" }
+ err := HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
+
+ if err == nil {
+ t.Fatalf("expected to receive error from Run, got none")
+ }
+
+ exitErr, ok := err.(*ExitError)
+
+ if !ok {
+ t.Fatalf("expected to receive a *ExitError")
+ }
+
+ if !strings.HasPrefix(exitErr.Error(), "ERROR unknown Action error") {
+ t.Fatalf("expected an unknown Action error, but got: %v", exitErr.Error())
+ }
+
+ if exitErr.ExitCode() != 2 {
+ t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode())
+ }
+}
+
+func TestHandleAction_WithInvalidFuncReturnSignature(t *testing.T) {
+ app := NewApp()
+ app.Action = func(_ *Context) (int, error) { return 0, nil }
+ err := HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
+
+ if err == nil {
+ t.Fatalf("expected to receive error from Run, got none")
+ }
+
+ exitErr, ok := err.(*ExitError)
+
+ if !ok {
+ t.Fatalf("expected to receive a *ExitError")
+ }
+
+ if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action signature") {
+ t.Fatalf("expected an invalid Action signature error, but got: %v", exitErr.Error())
+ }
+
+ if exitErr.ExitCode() != 2 {
+ t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode())
+ }
+}
+
+func TestHandleAction_WithUnknownPanic(t *testing.T) {
+ defer func() { refute(t, recover(), nil) }()
+
+ var fn ActionFunc
+
+ app := NewApp()
+ app.Action = func(ctx *Context) error {
+ fn(ctx)
+ return nil
+ }
+ HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
+}
diff --git a/vendor/github.com/codegangsta/cli/appveyor.yml b/vendor/github.com/codegangsta/cli/appveyor.yml
index 3ca7afa..173086e 100644
--- a/vendor/github.com/codegangsta/cli/appveyor.yml
+++ b/vendor/github.com/codegangsta/cli/appveyor.yml
@@ -2,15 +2,24 @@
os: Windows Server 2012 R2
+clone_folder: c:\gopath\src\github.com\urfave\cli
+
+environment:
+ GOPATH: C:\gopath
+ GOVERSION: 1.6
+ PYTHON: C:\Python27-x64
+ PYTHON_VERSION: 2.7.x
+ PYTHON_ARCH: 64
+ GFMXR_DEBUG: 1
+
install:
- - go version
- - go env
+- set PATH=%GOPATH%\bin;C:\go\bin;%PATH%
+- go version
+- go env
+- go get github.com/urfave/gfmxr/...
+- go get -v -t ./...
build_script:
- - cd %APPVEYOR_BUILD_FOLDER%
- - go vet ./...
- - go test -v ./...
-
-test: off
-
-deploy: off
+- python runtests vet
+- python runtests test
+- python runtests gfmxr
diff --git a/vendor/github.com/codegangsta/cli/category.go b/vendor/github.com/codegangsta/cli/category.go
index 7dbf218..1a60550 100644
--- a/vendor/github.com/codegangsta/cli/category.go
+++ b/vendor/github.com/codegangsta/cli/category.go
@@ -1,7 +1,9 @@
package cli
+// CommandCategories is a slice of *CommandCategory.
type CommandCategories []*CommandCategory
+// CommandCategory is a category containing commands.
type CommandCategory struct {
Name string
Commands Commands
@@ -19,6 +21,7 @@
c[i], c[j] = c[j], c[i]
}
+// AddCommand adds a command to a category.
func (c CommandCategories) AddCommand(category string, command Command) CommandCategories {
for _, commandCategory := range c {
if commandCategory.Name == category {
@@ -28,3 +31,14 @@
}
return append(c, &CommandCategory{Name: category, Commands: []Command{command}})
}
+
+// VisibleCommands returns a slice of the Commands with Hidden=false
+func (c *CommandCategory) VisibleCommands() []Command {
+ ret := []Command{}
+ for _, command := range c.Commands {
+ if !command.Hidden {
+ ret = append(ret, command)
+ }
+ }
+ return ret
+}
diff --git a/vendor/github.com/codegangsta/cli/cli.go b/vendor/github.com/codegangsta/cli/cli.go
index 31dc912..f0440c5 100644
--- a/vendor/github.com/codegangsta/cli/cli.go
+++ b/vendor/github.com/codegangsta/cli/cli.go
@@ -10,31 +10,10 @@
// app := cli.NewApp()
// app.Name = "greet"
// app.Usage = "say a greeting"
-// app.Action = func(c *cli.Context) {
+// app.Action = func(c *cli.Context) error {
// println("Greetings")
// }
//
// app.Run(os.Args)
// }
package cli
-
-import (
- "strings"
-)
-
-type MultiError struct {
- Errors []error
-}
-
-func NewMultiError(err ...error) MultiError {
- return MultiError{Errors: err}
-}
-
-func (m MultiError) Error() string {
- errs := make([]string, len(m.Errors))
- for i, err := range m.Errors {
- errs[i] = err.Error()
- }
-
- return strings.Join(errs, "\n")
-}
diff --git a/vendor/github.com/codegangsta/cli/command.go b/vendor/github.com/codegangsta/cli/command.go
index 1a05b54..8950cca 100644
--- a/vendor/github.com/codegangsta/cli/command.go
+++ b/vendor/github.com/codegangsta/cli/command.go
@@ -26,19 +26,20 @@
// The category the command is part of
Category string
// The function to call when checking for bash command completions
- BashComplete func(context *Context)
+ BashComplete BashCompleteFunc
// An action to execute before any sub-subcommands are run, but after the context is ready
// If a non-nil error is returned, no sub-subcommands are run
- Before func(context *Context) error
- // An action to execute after any subcommands are run, but before the subcommand has finished
+ Before BeforeFunc
+ // An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
- After func(context *Context) error
+ After AfterFunc
// The function to call when this command is invoked
- Action func(context *Context)
- // Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
- // This function is able to replace the original error messages.
- // If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
- OnUsageError func(context *Context, err error) error
+ Action interface{}
+ // TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
+ // of deprecation period has passed, maybe?
+
+ // Execute this function if a usage error occurs.
+ OnUsageError OnUsageErrorFunc
// List of child commands
Subcommands Commands
// List of flags to parse
@@ -47,13 +48,15 @@
SkipFlagParsing bool
// Boolean to hide built-in help command
HideHelp bool
+ // Boolean to hide this command from help or completion
+ Hidden bool
// Full name of command for help, defaults to full command name, including parent commands.
HelpName string
commandNamePath []string
}
-// Returns the full name of the command.
+// FullName returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path
func (c Command) FullName() string {
if c.commandNamePath == nil {
@@ -62,9 +65,10 @@
return strings.Join(c.commandNamePath, " ")
}
+// Commands is a slice of Command
type Commands []Command
-// Invokes the command given the context, parses ctx.Args() to generate command-specific flags
+// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c Command) Run(ctx *Context) (err error) {
if len(c.Subcommands) > 0 {
return c.startApp(ctx)
@@ -125,14 +129,14 @@
if err != nil {
if c.OnUsageError != nil {
- err := c.OnUsageError(ctx, err)
- return err
- } else {
- fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
- fmt.Fprintln(ctx.App.Writer)
- ShowCommandHelp(ctx, c.Name)
+ err := c.OnUsageError(ctx, err, false)
+ HandleExitCoder(err)
return err
}
+ fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
+ fmt.Fprintln(ctx.App.Writer)
+ ShowCommandHelp(ctx, c.Name)
+ return err
}
nerr := normalizeFlags(c.Flags, set)
@@ -142,6 +146,7 @@
ShowCommandHelp(ctx, c.Name)
return nerr
}
+
context := NewContext(ctx.App, set, ctx)
if checkCommandCompletions(context, c.Name) {
@@ -156,6 +161,7 @@
defer func() {
afterErr := c.After(context)
if afterErr != nil {
+ HandleExitCoder(err)
if err != nil {
err = NewMultiError(err, afterErr)
} else {
@@ -166,20 +172,26 @@
}
if c.Before != nil {
- err := c.Before(context)
+ err = c.Before(context)
if err != nil {
fmt.Fprintln(ctx.App.Writer, err)
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
+ HandleExitCoder(err)
return err
}
}
context.Command = c
- c.Action(context)
- return nil
+ err = HandleAction(c.Action, context)
+
+ if err != nil {
+ HandleExitCoder(err)
+ }
+ return err
}
+// Names returns the names including short names and aliases.
func (c Command) Names() []string {
names := []string{c.Name}
@@ -190,7 +202,7 @@
return append(names, c.Aliases...)
}
-// Returns true if Command.Name or Command.ShortName matches given name
+// HasName returns true if Command.Name or Command.ShortName matches given name
func (c Command) HasName(name string) bool {
for _, n := range c.Names() {
if n == name {
@@ -202,7 +214,7 @@
func (c Command) startApp(ctx *Context) error {
app := NewApp()
-
+ app.Metadata = ctx.App.Metadata
// set the name and usage
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
if c.HelpName == "" {
@@ -260,3 +272,8 @@
return app.RunAsSubcommand(ctx)
}
+
+// VisibleFlags returns a slice of the Flags with Hidden=false
+func (c Command) VisibleFlags() []Flag {
+ return visibleFlags(c.Flags)
+}
diff --git a/vendor/github.com/codegangsta/cli/command_test.go b/vendor/github.com/codegangsta/cli/command_test.go
index 827da1d..6608254 100644
--- a/vendor/github.com/codegangsta/cli/command_test.go
+++ b/vendor/github.com/codegangsta/cli/command_test.go
@@ -34,7 +34,7 @@
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
- Action: func(_ *Context) {},
+ Action: func(_ *Context) error { return nil },
}
command.SkipFlagParsing = c.skipFlagParsing
@@ -49,10 +49,14 @@
func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
app := NewApp()
app.Commands = []Command{
- Command{
- Name: "bar",
- Before: func(c *Context) error { return fmt.Errorf("before error") },
- After: func(c *Context) error { return fmt.Errorf("after error") },
+ {
+ Name: "bar",
+ Before: func(c *Context) error {
+ return fmt.Errorf("before error")
+ },
+ After: func(c *Context) error {
+ return fmt.Errorf("after error")
+ },
},
}
@@ -72,12 +76,12 @@
func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
app := NewApp()
app.Commands = []Command{
- Command{
- Name: "bar",
+ {
+ Name: "bar",
Flags: []Flag{
IntFlag{Name: "flag"},
},
- OnUsageError: func(c *Context, err error) error {
+ OnUsageError: func(c *Context, err error, _ bool) error {
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
}
diff --git a/vendor/github.com/codegangsta/cli/context.go b/vendor/github.com/codegangsta/cli/context.go
index b66d278..879bae5 100644
--- a/vendor/github.com/codegangsta/cli/context.go
+++ b/vendor/github.com/codegangsta/cli/context.go
@@ -21,57 +21,83 @@
parentContext *Context
}
-// Creates a new context. For use in when invoking an App or Command action.
+// NewContext creates a new context. For use in when invoking an App or Command action.
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
return &Context{App: app, flagSet: set, parentContext: parentCtx}
}
-// Looks up the value of a local int flag, returns 0 if no int flag exists
+// Int looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Int(name string) int {
return lookupInt(name, c.flagSet)
}
-// Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists
+// Int64 looks up the value of a local int flag, returns 0 if no int flag exists
+func (c *Context) Int64(name string) int64 {
+ return lookupInt64(name, c.flagSet)
+}
+
+// Uint looks up the value of a local int flag, returns 0 if no int flag exists
+func (c *Context) Uint(name string) uint {
+ return lookupUint(name, c.flagSet)
+}
+
+// Uint64 looks up the value of a local int flag, returns 0 if no int flag exists
+func (c *Context) Uint64(name string) uint64 {
+ return lookupUint64(name, c.flagSet)
+}
+
+// Duration looks up the value of a local time.Duration flag, returns 0 if no
+// time.Duration flag exists
func (c *Context) Duration(name string) time.Duration {
return lookupDuration(name, c.flagSet)
}
-// Looks up the value of a local float64 flag, returns 0 if no float64 flag exists
+// Float64 looks up the value of a local float64 flag, returns 0 if no float64
+// flag exists
func (c *Context) Float64(name string) float64 {
return lookupFloat64(name, c.flagSet)
}
-// Looks up the value of a local bool flag, returns false if no bool flag exists
+// Bool looks up the value of a local bool flag, returns false if no bool flag exists
func (c *Context) Bool(name string) bool {
return lookupBool(name, c.flagSet)
}
-// Looks up the value of a local boolT flag, returns false if no bool flag exists
+// BoolT looks up the value of a local boolT flag, returns false if no bool flag exists
func (c *Context) BoolT(name string) bool {
return lookupBoolT(name, c.flagSet)
}
-// Looks up the value of a local string flag, returns "" if no string flag exists
+// String looks up the value of a local string flag, returns "" if no string flag exists
func (c *Context) String(name string) string {
return lookupString(name, c.flagSet)
}
-// Looks up the value of a local string slice flag, returns nil if no string slice flag exists
+// StringSlice looks up the value of a local string slice flag, returns nil if no
+// string slice flag exists
func (c *Context) StringSlice(name string) []string {
return lookupStringSlice(name, c.flagSet)
}
-// Looks up the value of a local int slice flag, returns nil if no int slice flag exists
+// IntSlice looks up the value of a local int slice flag, returns nil if no int
+// slice flag exists
func (c *Context) IntSlice(name string) []int {
return lookupIntSlice(name, c.flagSet)
}
-// Looks up the value of a local generic flag, returns nil if no generic flag exists
+// Int64Slice looks up the value of a local int slice flag, returns nil if no int
+// slice flag exists
+func (c *Context) Int64Slice(name string) []int64 {
+ return lookupInt64Slice(name, c.flagSet)
+}
+
+// Generic looks up the value of a local generic flag, returns nil if no generic
+// flag exists
func (c *Context) Generic(name string) interface{} {
return lookupGeneric(name, c.flagSet)
}
-// Looks up the value of a global int flag, returns 0 if no int flag exists
+// GlobalInt looks up the value of a global int flag, returns 0 if no int flag exists
func (c *Context) GlobalInt(name string) int {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupInt(name, fs)
@@ -79,7 +105,41 @@
return 0
}
-// Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists
+// GlobalInt64 looks up the value of a global int flag, returns 0 if no int flag exists
+func (c *Context) GlobalInt64(name string) int64 {
+ if fs := lookupGlobalFlagSet(name, c); fs != nil {
+ return lookupInt64(name, fs)
+ }
+ return 0
+}
+
+// GlobalUint looks up the value of a global int flag, returns 0 if no int flag exists
+func (c *Context) GlobalUint(name string) uint {
+ if fs := lookupGlobalFlagSet(name, c); fs != nil {
+ return lookupUint(name, fs)
+ }
+ return 0
+}
+
+// GlobalUint64 looks up the value of a global int flag, returns 0 if no int flag exists
+func (c *Context) GlobalUint64(name string) uint64 {
+ if fs := lookupGlobalFlagSet(name, c); fs != nil {
+ return lookupUint64(name, fs)
+ }
+ return 0
+}
+
+// GlobalFloat64 looks up the value of a global float64 flag, returns float64(0)
+// if no float64 flag exists
+func (c *Context) GlobalFloat64(name string) float64 {
+ if fs := lookupGlobalFlagSet(name, c); fs != nil {
+ return lookupFloat64(name, fs)
+ }
+ return float64(0)
+}
+
+// GlobalDuration looks up the value of a global time.Duration flag, returns 0
+// if no time.Duration flag exists
func (c *Context) GlobalDuration(name string) time.Duration {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupDuration(name, fs)
@@ -87,7 +147,8 @@
return 0
}
-// Looks up the value of a global bool flag, returns false if no bool flag exists
+// GlobalBool looks up the value of a global bool flag, returns false if no bool
+// flag exists
func (c *Context) GlobalBool(name string) bool {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupBool(name, fs)
@@ -95,7 +156,17 @@
return false
}
-// Looks up the value of a global string flag, returns "" if no string flag exists
+// GlobalBoolT looks up the value of a global bool flag, returns true if no bool
+// flag exists
+func (c *Context) GlobalBoolT(name string) bool {
+ if fs := lookupGlobalFlagSet(name, c); fs != nil {
+ return lookupBoolT(name, fs)
+ }
+ return false
+}
+
+// GlobalString looks up the value of a global string flag, returns "" if no
+// string flag exists
func (c *Context) GlobalString(name string) string {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupString(name, fs)
@@ -103,7 +174,8 @@
return ""
}
-// Looks up the value of a global string slice flag, returns nil if no string slice flag exists
+// GlobalStringSlice looks up the value of a global string slice flag, returns
+// nil if no string slice flag exists
func (c *Context) GlobalStringSlice(name string) []string {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupStringSlice(name, fs)
@@ -111,7 +183,8 @@
return nil
}
-// Looks up the value of a global int slice flag, returns nil if no int slice flag exists
+// GlobalIntSlice looks up the value of a global int slice flag, returns nil if
+// no int slice flag exists
func (c *Context) GlobalIntSlice(name string) []int {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupIntSlice(name, fs)
@@ -119,7 +192,17 @@
return nil
}
-// Looks up the value of a global generic flag, returns nil if no generic flag exists
+// GlobalInt64Slice looks up the value of a global int slice flag, returns nil if
+// no int slice flag exists
+func (c *Context) GlobalInt64Slice(name string) []int64 {
+ if fs := lookupGlobalFlagSet(name, c); fs != nil {
+ return lookupInt64Slice(name, fs)
+ }
+ return nil
+}
+
+// GlobalGeneric looks up the value of a global generic flag, returns nil if no
+// generic flag exists
func (c *Context) GlobalGeneric(name string) interface{} {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupGeneric(name, fs)
@@ -127,12 +210,22 @@
return nil
}
-// Returns the number of flags set
+// NumFlags returns the number of flags set
func (c *Context) NumFlags() int {
return c.flagSet.NFlag()
}
-// Determines if the flag was actually set
+// Set sets a context flag to a value.
+func (c *Context) Set(name, value string) error {
+ return c.flagSet.Set(name, value)
+}
+
+// GlobalSet sets a context flag to a value on the global flagset
+func (c *Context) GlobalSet(name, value string) error {
+ return globalContext(c).flagSet.Set(name, value)
+}
+
+// IsSet determines if the flag was actually set
func (c *Context) IsSet(name string) bool {
if c.setFlags == nil {
c.setFlags = make(map[string]bool)
@@ -143,7 +236,7 @@
return c.setFlags[name] == true
}
-// Determines if the global flag was actually set
+// GlobalIsSet determines if the global flag was actually set
func (c *Context) GlobalIsSet(name string) bool {
if c.globalSetFlags == nil {
c.globalSetFlags = make(map[string]bool)
@@ -160,7 +253,7 @@
return c.globalSetFlags[name]
}
-// Returns a slice of flag names used in this context.
+// FlagNames returns a slice of flag names used in this context.
func (c *Context) FlagNames() (names []string) {
for _, flag := range c.Command.Flags {
name := strings.Split(flag.GetName(), ",")[0]
@@ -172,7 +265,7 @@
return
}
-// Returns a slice of global flag names used by the app.
+// GlobalFlagNames returns a slice of global flag names used by the app.
func (c *Context) GlobalFlagNames() (names []string) {
for _, flag := range c.App.Flags {
name := strings.Split(flag.GetName(), ",")[0]
@@ -184,25 +277,26 @@
return
}
-// Returns the parent context, if any
+// Parent returns the parent context, if any
func (c *Context) Parent() *Context {
return c.parentContext
}
+// Args contains apps console arguments
type Args []string
-// Returns the command line arguments associated with the context.
+// Args returns the command line arguments associated with the context.
func (c *Context) Args() Args {
args := Args(c.flagSet.Args())
return args
}
-// Returns the number of the command line arguments.
+// NArg returns the number of the command line arguments.
func (c *Context) NArg() int {
return len(c.Args())
}
-// Returns the nth argument, or else a blank string
+// Get returns the nth argument, or else a blank string
func (a Args) Get(n int) string {
if len(a) > n {
return a[n]
@@ -210,12 +304,12 @@
return ""
}
-// Returns the first argument, or else a blank string
+// First returns the first argument, or else a blank string
func (a Args) First() string {
return a.Get(0)
}
-// Return the rest of the arguments (not the first one)
+// Tail returns the rest of the arguments (not the first one)
// or else an empty string slice
func (a Args) Tail() []string {
if len(a) >= 2 {
@@ -224,12 +318,12 @@
return []string{}
}
-// Checks if there are any arguments present
+// Present checks if there are any arguments present
func (a Args) Present() bool {
return len(a) != 0
}
-// Swaps arguments at the given indexes
+// Swap swaps arguments at the given indexes
func (a Args) Swap(from, to int) error {
if from >= len(a) || to >= len(a) {
return errors.New("index out of range")
@@ -238,6 +332,19 @@
return nil
}
+func globalContext(ctx *Context) *Context {
+ if ctx == nil {
+ return nil
+ }
+
+ for {
+ if ctx.parentContext == nil {
+ return ctx
+ }
+ ctx = ctx.parentContext
+ }
+}
+
func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet {
if ctx.parentContext != nil {
ctx = ctx.parentContext
@@ -253,7 +360,46 @@
func lookupInt(name string, set *flag.FlagSet) int {
f := set.Lookup(name)
if f != nil {
- val, err := strconv.Atoi(f.Value.String())
+ val, err := strconv.ParseInt(f.Value.String(), 0, 64)
+ if err != nil {
+ return 0
+ }
+ return int(val)
+ }
+
+ return 0
+}
+
+func lookupInt64(name string, set *flag.FlagSet) int64 {
+ f := set.Lookup(name)
+ if f != nil {
+ val, err := strconv.ParseInt(f.Value.String(), 0, 64)
+ if err != nil {
+ return 0
+ }
+ return val
+ }
+
+ return 0
+}
+
+func lookupUint(name string, set *flag.FlagSet) uint {
+ f := set.Lookup(name)
+ if f != nil {
+ val, err := strconv.ParseUint(f.Value.String(), 0, 64)
+ if err != nil {
+ return 0
+ }
+ return uint(val)
+ }
+
+ return 0
+}
+
+func lookupUint64(name string, set *flag.FlagSet) uint64 {
+ f := set.Lookup(name)
+ if f != nil {
+ val, err := strconv.ParseUint(f.Value.String(), 0, 64)
if err != nil {
return 0
}
@@ -317,6 +463,16 @@
return nil
}
+func lookupInt64Slice(name string, set *flag.FlagSet) []int64 {
+ f := set.Lookup(name)
+ if f != nil {
+ return (f.Value.(*Int64Slice)).Value()
+
+ }
+
+ return nil
+}
+
func lookupGeneric(name string, set *flag.FlagSet) interface{} {
f := set.Lookup(name)
if f != nil {
diff --git a/vendor/github.com/codegangsta/cli/context_test.go b/vendor/github.com/codegangsta/cli/context_test.go
index b8ab37d..5c68fdd 100644
--- a/vendor/github.com/codegangsta/cli/context_test.go
+++ b/vendor/github.com/codegangsta/cli/context_test.go
@@ -9,14 +9,30 @@
func TestNewContext(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Int("myflag", 12, "doc")
+ set.Int64("myflagInt64", int64(12), "doc")
+ set.Uint("myflagUint", uint(93), "doc")
+ set.Uint64("myflagUint64", uint64(93), "doc")
+ set.Float64("myflag64", float64(17), "doc")
globalSet := flag.NewFlagSet("test", 0)
globalSet.Int("myflag", 42, "doc")
+ globalSet.Int64("myflagInt64", int64(42), "doc")
+ globalSet.Uint("myflagUint", uint(33), "doc")
+ globalSet.Uint64("myflagUint64", uint64(33), "doc")
+ globalSet.Float64("myflag64", float64(47), "doc")
globalCtx := NewContext(nil, globalSet, nil)
command := Command{Name: "mycommand"}
c := NewContext(nil, set, globalCtx)
c.Command = command
expect(t, c.Int("myflag"), 12)
+ expect(t, c.Int64("myflagInt64"), int64(12))
+ expect(t, c.Uint("myflagUint"), uint(93))
+ expect(t, c.Uint64("myflagUint64"), uint64(93))
+ expect(t, c.Float64("myflag64"), float64(17))
expect(t, c.GlobalInt("myflag"), 42)
+ expect(t, c.GlobalInt64("myflagInt64"), int64(42))
+ expect(t, c.GlobalUint("myflagUint"), uint(33))
+ expect(t, c.GlobalUint64("myflagUint64"), uint64(33))
+ expect(t, c.GlobalFloat64("myflag64"), float64(47))
expect(t, c.Command.Name, "mycommand")
}
@@ -27,6 +43,58 @@
expect(t, c.Int("myflag"), 12)
}
+func TestContext_Int64(t *testing.T) {
+ set := flag.NewFlagSet("test", 0)
+ set.Int64("myflagInt64", 12, "doc")
+ c := NewContext(nil, set, nil)
+ expect(t, c.Int64("myflagInt64"), int64(12))
+}
+
+func TestContext_Uint(t *testing.T) {
+ set := flag.NewFlagSet("test", 0)
+ set.Uint("myflagUint", uint(13), "doc")
+ c := NewContext(nil, set, nil)
+ expect(t, c.Uint("myflagUint"), uint(13))
+}
+
+func TestContext_Uint64(t *testing.T) {
+ set := flag.NewFlagSet("test", 0)
+ set.Uint64("myflagUint64", uint64(9), "doc")
+ c := NewContext(nil, set, nil)
+ expect(t, c.Uint64("myflagUint64"), uint64(9))
+}
+
+func TestContext_GlobalInt(t *testing.T) {
+ set := flag.NewFlagSet("test", 0)
+ set.Int("myflag", 12, "doc")
+ c := NewContext(nil, set, nil)
+ expect(t, c.GlobalInt("myflag"), 12)
+ expect(t, c.GlobalInt("nope"), 0)
+}
+
+func TestContext_GlobalInt64(t *testing.T) {
+ set := flag.NewFlagSet("test", 0)
+ set.Int64("myflagInt64", 12, "doc")
+ c := NewContext(nil, set, nil)
+ expect(t, c.GlobalInt64("myflagInt64"), int64(12))
+ expect(t, c.GlobalInt64("nope"), int64(0))
+}
+
+func TestContext_Float64(t *testing.T) {
+ set := flag.NewFlagSet("test", 0)
+ set.Float64("myflag", float64(17), "doc")
+ c := NewContext(nil, set, nil)
+ expect(t, c.Float64("myflag"), float64(17))
+}
+
+func TestContext_GlobalFloat64(t *testing.T) {
+ set := flag.NewFlagSet("test", 0)
+ set.Float64("myflag", float64(17), "doc")
+ c := NewContext(nil, set, nil)
+ expect(t, c.GlobalFloat64("myflag"), float64(17))
+ expect(t, c.GlobalFloat64("nope"), float64(0))
+}
+
func TestContext_Duration(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Duration("myflag", time.Duration(12*time.Second), "doc")
@@ -55,6 +123,30 @@
expect(t, c.BoolT("myflag"), true)
}
+func TestContext_GlobalBool(t *testing.T) {
+ set := flag.NewFlagSet("test", 0)
+
+ globalSet := flag.NewFlagSet("test-global", 0)
+ globalSet.Bool("myflag", false, "doc")
+ globalCtx := NewContext(nil, globalSet, nil)
+
+ c := NewContext(nil, set, globalCtx)
+ expect(t, c.GlobalBool("myflag"), false)
+ expect(t, c.GlobalBool("nope"), false)
+}
+
+func TestContext_GlobalBoolT(t *testing.T) {
+ set := flag.NewFlagSet("test", 0)
+
+ globalSet := flag.NewFlagSet("test-global", 0)
+ globalSet.Bool("myflag", true, "doc")
+ globalCtx := NewContext(nil, globalSet, nil)
+
+ c := NewContext(nil, set, globalCtx)
+ expect(t, c.GlobalBoolT("myflag"), true)
+ expect(t, c.GlobalBoolT("nope"), false)
+}
+
func TestContext_Args(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Bool("myflag", false, "doc")
@@ -119,3 +211,87 @@
globalSet.Parse([]string{"--myflagGlobal"})
expect(t, c.NumFlags(), 2)
}
+
+func TestContext_GlobalFlag(t *testing.T) {
+ var globalFlag string
+ var globalFlagSet bool
+ app := NewApp()
+ app.Flags = []Flag{
+ StringFlag{Name: "global, g", Usage: "global"},
+ }
+ app.Action = func(c *Context) error {
+ globalFlag = c.GlobalString("global")
+ globalFlagSet = c.GlobalIsSet("global")
+ return nil
+ }
+ app.Run([]string{"command", "-g", "foo"})
+ expect(t, globalFlag, "foo")
+ expect(t, globalFlagSet, true)
+
+}
+
+func TestContext_GlobalFlagsInSubcommands(t *testing.T) {
+ subcommandRun := false
+ parentFlag := false
+ app := NewApp()
+
+ app.Flags = []Flag{
+ BoolFlag{Name: "debug, d", Usage: "Enable debugging"},
+ }
+
+ app.Commands = []Command{
+ {
+ Name: "foo",
+ Flags: []Flag{
+ BoolFlag{Name: "parent, p", Usage: "Parent flag"},
+ },
+ Subcommands: []Command{
+ {
+ Name: "bar",
+ Action: func(c *Context) error {
+ if c.GlobalBool("debug") {
+ subcommandRun = true
+ }
+ if c.GlobalBool("parent") {
+ parentFlag = true
+ }
+ return nil
+ },
+ },
+ },
+ },
+ }
+
+ app.Run([]string{"command", "-d", "foo", "-p", "bar"})
+
+ expect(t, subcommandRun, true)
+ expect(t, parentFlag, true)
+}
+
+func TestContext_Set(t *testing.T) {
+ set := flag.NewFlagSet("test", 0)
+ set.Int("int", 5, "an int")
+ c := NewContext(nil, set, nil)
+
+ c.Set("int", "1")
+ expect(t, c.Int("int"), 1)
+}
+
+func TestContext_GlobalSet(t *testing.T) {
+ gSet := flag.NewFlagSet("test", 0)
+ gSet.Int("int", 5, "an int")
+
+ set := flag.NewFlagSet("sub", 0)
+ set.Int("int", 3, "an int")
+
+ pc := NewContext(nil, gSet, nil)
+ c := NewContext(nil, set, pc)
+
+ c.Set("int", "1")
+ expect(t, c.Int("int"), 1)
+ expect(t, c.GlobalInt("int"), 5)
+
+ c.GlobalSet("int", "1")
+ expect(t, c.Int("int"), 1)
+ expect(t, c.GlobalInt("int"), 1)
+}
diff --git a/vendor/github.com/codegangsta/cli/errors.go b/vendor/github.com/codegangsta/cli/errors.go
new file mode 100644
index 0000000..ea551be
--- /dev/null
+++ b/vendor/github.com/codegangsta/cli/errors.go
@@ -0,0 +1,92 @@
+package cli
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+)
+
+// OsExiter is the function used when the app exits. If not set defaults to os.Exit.
+var OsExiter = os.Exit
+
+// ErrWriter is used to write errors to the user. This can be anything
+// implementing the io.Writer interface and defaults to os.Stderr.
+var ErrWriter io.Writer = os.Stderr
+
+// MultiError is an error that wraps multiple errors.
+type MultiError struct {
+ Errors []error
+}
+
+// NewMultiError creates a new MultiError. Pass in one or more errors.
+func NewMultiError(err ...error) MultiError {
+ return MultiError{Errors: err}
+}
+
+// Error implents the error interface.
+func (m MultiError) Error() string {
+ errs := make([]string, len(m.Errors))
+ for i, err := range m.Errors {
+ errs[i] = err.Error()
+ }
+
+ return strings.Join(errs, "\n")
+}
+
+// ExitCoder is the interface checked by `App` and `Command` for a custom exit
+// code
+type ExitCoder interface {
+ error
+ ExitCode() int
+}
+
+// ExitError fulfills both the builtin `error` interface and `ExitCoder`
+type ExitError struct {
+ exitCode int
+ message string
+}
+
+// NewExitError makes a new *ExitError
+func NewExitError(message string, exitCode int) *ExitError {
+ return &ExitError{
+ exitCode: exitCode,
+ message: message,
+ }
+}
+
+// Error returns the string message, fulfilling the interface required by
+// `error`
+func (ee *ExitError) Error() string {
+ return ee.message
+}
+
+// ExitCode returns the exit code, fulfilling the interface required by
+// `ExitCoder`
+func (ee *ExitError) ExitCode() int {
+ return ee.exitCode
+}
+
+// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
+// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
+// given exit code. If the given error is a MultiError, then this func is
+// called on all members of the Errors slice.
+func HandleExitCoder(err error) {
+ if err == nil {
+ return
+ }
+
+ if exitErr, ok := err.(ExitCoder); ok {
+ if err.Error() != "" {
+ fmt.Fprintln(ErrWriter, err)
+ }
+ OsExiter(exitErr.ExitCode())
+ return
+ }
+
+ if multiErr, ok := err.(MultiError); ok {
+ for _, merr := range multiErr.Errors {
+ HandleExitCoder(merr)
+ }
+ }
+}
diff --git a/vendor/github.com/codegangsta/cli/errors_test.go b/vendor/github.com/codegangsta/cli/errors_test.go
new file mode 100644
index 0000000..8f5f284
--- /dev/null
+++ b/vendor/github.com/codegangsta/cli/errors_test.go
@@ -0,0 +1,60 @@
+package cli
+
+import (
+ "errors"
+ "os"
+ "testing"
+)
+
+func TestHandleExitCoder_nil(t *testing.T) {
+ exitCode := 0
+ called := false
+
+ OsExiter = func(rc int) {
+ exitCode = rc
+ called = true
+ }
+
+ defer func() { OsExiter = os.Exit }()
+
+ HandleExitCoder(nil)
+
+ expect(t, exitCode, 0)
+ expect(t, called, false)
+}
+
+func TestHandleExitCoder_ExitCoder(t *testing.T) {
+ exitCode := 0
+ called := false
+
+ OsExiter = func(rc int) {
+ exitCode = rc
+ called = true
+ }
+
+ defer func() { OsExiter = os.Exit }()
+
+ HandleExitCoder(NewExitError("galactic perimeter breach", 9))
+
+ expect(t, exitCode, 9)
+ expect(t, called, true)
+}
+
+func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) {
+ exitCode := 0
+ called := false
+
+ OsExiter = func(rc int) {
+ exitCode = rc
+ called = true
+ }
+
+ defer func() { OsExiter = os.Exit }()
+
+ exitErr := NewExitError("galactic perimeter breach", 9)
+ err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr)
+ HandleExitCoder(err)
+
+ expect(t, exitCode, 9)
+ expect(t, called, true)
+}
diff --git a/vendor/github.com/codegangsta/cli/flag.go b/vendor/github.com/codegangsta/cli/flag.go
index e951c2d..f8a28d1 100644
--- a/vendor/github.com/codegangsta/cli/flag.go
+++ b/vendor/github.com/codegangsta/cli/flag.go
@@ -4,24 +4,28 @@
"flag"
"fmt"
"os"
+ "reflect"
"runtime"
"strconv"
"strings"
"time"
)
-// This flag enables bash-completion for all commands and subcommands
+const defaultPlaceholder = "value"
+
+// BashCompletionFlag enables bash-completion for all commands and subcommands
var BashCompletionFlag = BoolFlag{
- Name: "generate-bash-completion",
+ Name: "generate-bash-completion",
+ Hidden: true,
}
-// This flag prints the version for the application
+// VersionFlag prints the version for the application
var VersionFlag = BoolFlag{
Name: "version, v",
Usage: "print the version",
}
-// This flag prints the help for all commands and subcommands
+// HelpFlag prints the help for all commands and subcommands
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
// unless HideHelp is set to true)
var HelpFlag = BoolFlag{
@@ -29,6 +33,10 @@
Usage: "show help",
}
+// FlagStringer converts a flag definition to a string. This is used by help
+// to display a flag.
+var FlagStringer FlagStringFunc = stringifyFlag
+
// Flag is a common interface related to parsing flags in cli.
// For more advanced flag parsing techniques, it is recommended that
// this interface be implemented.
@@ -68,24 +76,14 @@
Value Generic
Usage string
EnvVar string
+ Hidden bool
}
// String returns the string representation of the generic flag to display the
// help text to the user (uses the String() method of the generic flag to show
// the value)
func (f GenericFlag) String() string {
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
-}
-
-func (f GenericFlag) FormatValueHelp() string {
- if f.Value == nil {
- return ""
- }
- s := f.Value.String()
- if len(s) == 0 {
- return ""
- }
- return fmt.Sprintf("\"%s\"", s)
+ return FlagStringer(f)
}
// Apply takes the flagset and calls Set on the generic flag with the value
@@ -107,6 +105,7 @@
})
}
+// GetName returns the name of a flag.
func (f GenericFlag) GetName() string {
return f.Name
}
@@ -130,20 +129,19 @@
return *f
}
-// StringSlice is a string flag that can be specified multiple times on the
+// StringSliceFlag is a string flag that can be specified multiple times on the
// command-line
type StringSliceFlag struct {
Name string
Value *StringSlice
Usage string
EnvVar string
+ Hidden bool
}
// String returns the usage
func (f StringSliceFlag) String() string {
- firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
- pref := prefixFor(firstName)
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
+ return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
@@ -171,11 +169,12 @@
})
}
+// GetName returns the name of a flag.
func (f StringSliceFlag) GetName() string {
return f.Name
}
-// StringSlice is an opaque type for []int to satisfy flag.Value
+// IntSlice is an opaque type for []int to satisfy flag.Value
type IntSlice []int
// Set parses the value into an integer and appends it to the list of values
@@ -183,15 +182,14 @@
tmp, err := strconv.Atoi(value)
if err != nil {
return err
- } else {
- *f = append(*f, tmp)
}
+ *f = append(*f, tmp)
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (f *IntSlice) String() string {
- return fmt.Sprintf("%d", *f)
+ return fmt.Sprintf("%#v", *f)
}
// Value returns the slice of ints set by this flag
@@ -206,13 +204,12 @@
Value *IntSlice
Usage string
EnvVar string
+ Hidden bool
}
// String returns the usage
func (f IntSliceFlag) String() string {
- firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
- pref := prefixFor(firstName)
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
+ return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
@@ -226,7 +223,7 @@
s = strings.TrimSpace(s)
err := newVal.Set(s)
if err != nil {
- fmt.Fprintf(os.Stderr, err.Error())
+ fmt.Fprintf(ErrWriter, err.Error())
}
}
f.Value = newVal
@@ -243,21 +240,94 @@
})
}
+// GetName returns the name of the flag.
func (f IntSliceFlag) GetName() string {
return f.Name
}
+// Int64Slice is an opaque type for []int to satisfy flag.Value
+type Int64Slice []int64
+
+// Set parses the value into an integer and appends it to the list of values
+func (f *Int64Slice) Set(value string) error {
+ tmp, err := strconv.ParseInt(value, 10, 64)
+ if err != nil {
+ return err
+ }
+ *f = append(*f, tmp)
+ return nil
+}
+
+// String returns a readable representation of this value (for usage defaults)
+func (f *Int64Slice) String() string {
+ return fmt.Sprintf("%#v", *f)
+}
+
+// Value returns the slice of ints set by this flag
+func (f *Int64Slice) Value() []int64 {
+ return *f
+}
+
+// Int64SliceFlag is an int flag that can be specified multiple times on the
+// command-line
+type Int64SliceFlag struct {
+ Name string
+ Value *Int64Slice
+ Usage string
+ EnvVar string
+ Hidden bool
+}
+
+// String returns the usage
+func (f Int64SliceFlag) String() string {
+ return FlagStringer(f)
+}
+
+// Apply populates the flag given the flag set and environment
+func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
+ if f.EnvVar != "" {
+ for _, envVar := range strings.Split(f.EnvVar, ",") {
+ envVar = strings.TrimSpace(envVar)
+ if envVal := os.Getenv(envVar); envVal != "" {
+ newVal := &Int64Slice{}
+ for _, s := range strings.Split(envVal, ",") {
+ s = strings.TrimSpace(s)
+ err := newVal.Set(s)
+ if err != nil {
+ fmt.Fprintf(ErrWriter, err.Error())
+ }
+ }
+ f.Value = newVal
+ break
+ }
+ }
+ }
+
+ eachName(f.Name, func(name string) {
+ if f.Value == nil {
+ f.Value = &Int64Slice{}
+ }
+ set.Var(f.Value, name, f.Usage)
+ })
+}
+
+// GetName returns the name of the flag.
+func (f Int64SliceFlag) GetName() string {
+ return f.Name
+}
+
// BoolFlag is a switch that defaults to false
type BoolFlag struct {
Name string
Usage string
EnvVar string
Destination *bool
+ Hidden bool
}
// String returns a readable representation of this value (for usage defaults)
func (f BoolFlag) String() string {
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
+ return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
@@ -285,6 +355,7 @@
})
}
+// GetName returns the name of the flag.
func (f BoolFlag) GetName() string {
return f.Name
}
@@ -296,11 +367,12 @@
Usage string
EnvVar string
Destination *bool
+ Hidden bool
}
// String returns a readable representation of this value (for usage defaults)
func (f BoolTFlag) String() string {
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
+ return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
@@ -328,6 +400,7 @@
})
}
+// GetName returns the name of the flag.
func (f BoolTFlag) GetName() string {
return f.Name
}
@@ -339,19 +412,12 @@
Usage string
EnvVar string
Destination *string
+ Hidden bool
}
// String returns the usage
func (f StringFlag) String() string {
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
-}
-
-func (f StringFlag) FormatValueHelp() string {
- s := f.Value
- if len(s) == 0 {
- return ""
- }
- return fmt.Sprintf("\"%s\"", s)
+ return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
@@ -375,23 +441,24 @@
})
}
+// GetName returns the name of the flag.
func (f StringFlag) GetName() string {
return f.Name
}
// IntFlag is a flag that takes an integer
-// Errors if the value provided cannot be parsed
type IntFlag struct {
Name string
Value int
Usage string
EnvVar string
Destination *int
+ Hidden bool
}
// String returns the usage
func (f IntFlag) String() string {
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
+ return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
@@ -418,10 +485,143 @@
})
}
+// GetName returns the name of the flag.
func (f IntFlag) GetName() string {
return f.Name
}
+// Int64Flag is a flag that takes a 64-bit integer
+type Int64Flag struct {
+ Name string
+ Value int64
+ Usage string
+ EnvVar string
+ Destination *int64
+ Hidden bool
+}
+
+// String returns the usage
+func (f Int64Flag) String() string {
+ return FlagStringer(f)
+}
+
+// Apply populates the flag given the flag set and environment
+func (f Int64Flag) Apply(set *flag.FlagSet) {
+ if f.EnvVar != "" {
+ for _, envVar := range strings.Split(f.EnvVar, ",") {
+ envVar = strings.TrimSpace(envVar)
+ if envVal := os.Getenv(envVar); envVal != "" {
+ envValInt, err := strconv.ParseInt(envVal, 0, 64)
+ if err == nil {
+ f.Value = envValInt
+ break
+ }
+ }
+ }
+ }
+
+ eachName(f.Name, func(name string) {
+ if f.Destination != nil {
+ set.Int64Var(f.Destination, name, f.Value, f.Usage)
+ return
+ }
+ set.Int64(name, f.Value, f.Usage)
+ })
+}
+
+// GetName returns the name of the flag.
+func (f Int64Flag) GetName() string {
+ return f.Name
+}
+
+// UintFlag is a flag that takes an unsigned integer
+type UintFlag struct {
+ Name string
+ Value uint
+ Usage string
+ EnvVar string
+ Destination *uint
+ Hidden bool
+}
+
+// String returns the usage
+func (f UintFlag) String() string {
+ return FlagStringer(f)
+}
+
+// Apply populates the flag given the flag set and environment
+func (f UintFlag) Apply(set *flag.FlagSet) {
+ if f.EnvVar != "" {
+ for _, envVar := range strings.Split(f.EnvVar, ",") {
+ envVar = strings.TrimSpace(envVar)
+ if envVal := os.Getenv(envVar); envVal != "" {
+ envValInt, err := strconv.ParseUint(envVal, 0, 64)
+ if err == nil {
+ f.Value = uint(envValInt)
+ break
+ }
+ }
+ }
+ }
+
+ eachName(f.Name, func(name string) {
+ if f.Destination != nil {
+ set.UintVar(f.Destination, name, f.Value, f.Usage)
+ return
+ }
+ set.Uint(name, f.Value, f.Usage)
+ })
+}
+
+// GetName returns the name of the flag.
+func (f UintFlag) GetName() string {
+ return f.Name
+}
+
+// Uint64Flag is a flag that takes an unsigned 64-bit integer
+type Uint64Flag struct {
+ Name string
+ Value uint64
+ Usage string
+ EnvVar string
+ Destination *uint64
+ Hidden bool
+}
+
+// String returns the usage
+func (f Uint64Flag) String() string {
+ return FlagStringer(f)
+}
+
+// Apply populates the flag given the flag set and environment
+func (f Uint64Flag) Apply(set *flag.FlagSet) {
+ if f.EnvVar != "" {
+ for _, envVar := range strings.Split(f.EnvVar, ",") {
+ envVar = strings.TrimSpace(envVar)
+ if envVal := os.Getenv(envVar); envVal != "" {
+ envValInt, err := strconv.ParseUint(envVal, 0, 64)
+ if err == nil {
+ f.Value = uint64(envValInt)
+ break
+ }
+ }
+ }
+ }
+
+ eachName(f.Name, func(name string) {
+ if f.Destination != nil {
+ set.Uint64Var(f.Destination, name, f.Value, f.Usage)
+ return
+ }
+ set.Uint64(name, f.Value, f.Usage)
+ })
+}
+
+// GetName returns the name of the flag.
+func (f Uint64Flag) GetName() string {
+ return f.Name
+}
+
// DurationFlag is a flag that takes a duration specified in Go's duration
// format: https://golang.org/pkg/time/#ParseDuration
type DurationFlag struct {
@@ -430,11 +630,12 @@
Usage string
EnvVar string
Destination *time.Duration
+ Hidden bool
}
// String returns a readable representation of this value (for usage defaults)
func (f DurationFlag) String() string {
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
+ return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
@@ -461,23 +662,24 @@
})
}
+// GetName returns the name of the flag.
func (f DurationFlag) GetName() string {
return f.Name
}
// Float64Flag is a flag that takes an float value
-// Errors if the value provided cannot be parsed
type Float64Flag struct {
Name string
Value float64
Usage string
EnvVar string
Destination *float64
+ Hidden bool
}
// String returns the usage
func (f Float64Flag) String() string {
- return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
+ return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
@@ -503,10 +705,21 @@
})
}
+// GetName returns the name of the flag.
func (f Float64Flag) GetName() string {
return f.Name
}
+func visibleFlags(fl []Flag) []Flag {
+ visible := []Flag{}
+ for _, flag := range fl {
+ if !flagValue(flag).FieldByName("Hidden").Bool() {
+ visible = append(visible, flag)
+ }
+ }
+ return visible
+}
+
func prefixFor(name string) (prefix string) {
if len(name) == 1 {
prefix = "-"
@@ -517,16 +730,37 @@
return
}
-func prefixedNames(fullName string) (prefixed string) {
+// Returns the placeholder, if any, and the unquoted usage string.
+func unquoteUsage(usage string) (string, string) {
+ for i := 0; i < len(usage); i++ {
+ if usage[i] == '`' {
+ for j := i + 1; j < len(usage); j++ {
+ if usage[j] == '`' {
+ name := usage[i+1 : j]
+ usage = usage[:i] + name + usage[j+1:]
+ return name, usage
+ }
+ }
+ break
+ }
+ }
+ return "", usage
+}
+
+func prefixedNames(fullName, placeholder string) string {
+ var prefixed string
parts := strings.Split(fullName, ",")
for i, name := range parts {
name = strings.Trim(name, " ")
prefixed += prefixFor(name) + name
+ if placeholder != "" {
+ prefixed += " " + placeholder
+ }
if i < len(parts)-1 {
prefixed += ", "
}
}
- return
+ return prefixed
}
func withEnvHint(envVar, str string) string {
@@ -544,3 +778,105 @@
}
return str + envText
}
+
+func flagValue(f Flag) reflect.Value {
+ fv := reflect.ValueOf(f)
+ for fv.Kind() == reflect.Ptr {
+ fv = reflect.Indirect(fv)
+ }
+ return fv
+}
+
+func stringifyFlag(f Flag) string {
+ fv := flagValue(f)
+
+ switch f.(type) {
+ case IntSliceFlag:
+ return withEnvHint(fv.FieldByName("EnvVar").String(),
+ stringifyIntSliceFlag(f.(IntSliceFlag)))
+ case Int64SliceFlag:
+ return withEnvHint(fv.FieldByName("EnvVar").String(),
+ stringifyInt64SliceFlag(f.(Int64SliceFlag)))
+ case StringSliceFlag:
+ return withEnvHint(fv.FieldByName("EnvVar").String(),
+ stringifyStringSliceFlag(f.(StringSliceFlag)))
+ }
+
+ placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String())
+
+ needsPlaceholder := false
+ defaultValueString := ""
+ val := fv.FieldByName("Value")
+
+ if val.IsValid() {
+ needsPlaceholder = true
+ defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface())
+
+ if val.Kind() == reflect.String && val.String() != "" {
+ defaultValueString = fmt.Sprintf(" (default: %q)", val.String())
+ }
+ }
+
+ if defaultValueString == " (default: )" {
+ defaultValueString = ""
+ }
+
+ if needsPlaceholder && placeholder == "" {
+ placeholder = defaultPlaceholder
+ }
+
+ usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString))
+
+ return withEnvHint(fv.FieldByName("EnvVar").String(),
+ fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault))
+}
+
+func stringifyIntSliceFlag(f IntSliceFlag) string {
+ defaultVals := []string{}
+ if f.Value != nil && len(f.Value.Value()) > 0 {
+ for _, i := range f.Value.Value() {
+ defaultVals = append(defaultVals, fmt.Sprintf("%d", i))
+ }
+ }
+
+ return stringifySliceFlag(f.Usage, f.Name, defaultVals)
+}
+
+func stringifyInt64SliceFlag(f Int64SliceFlag) string {
+ defaultVals := []string{}
+ if f.Value != nil && len(f.Value.Value()) > 0 {
+ for _, i := range f.Value.Value() {
+ defaultVals = append(defaultVals, fmt.Sprintf("%d", i))
+ }
+ }
+
+ return stringifySliceFlag(f.Usage, f.Name, defaultVals)
+}
+
+func stringifyStringSliceFlag(f StringSliceFlag) string {
+ defaultVals := []string{}
+ if f.Value != nil && len(f.Value.Value()) > 0 {
+ for _, s := range f.Value.Value() {
+ if len(s) > 0 {
+ defaultVals = append(defaultVals, fmt.Sprintf("%q", s))
+ }
+ }
+ }
+
+ return stringifySliceFlag(f.Usage, f.Name, defaultVals)
+}
+
+func stringifySliceFlag(usage, name string, defaultVals []string) string {
+ placeholder, usage := unquoteUsage(usage)
+ if placeholder == "" {
+ placeholder = defaultPlaceholder
+ }
+
+ defaultVal := ""
+ if len(defaultVals) > 0 {
+ defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", "))
+ }
+
+ usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
+ return fmt.Sprintf("%s\t%s", prefixedNames(name, placeholder), usageWithDefault)
+}
diff --git a/vendor/github.com/codegangsta/cli/flag_test.go b/vendor/github.com/codegangsta/cli/flag_test.go
index 3caa70a..a7afcc4 100644
--- a/vendor/github.com/codegangsta/cli/flag_test.go
+++ b/vendor/github.com/codegangsta/cli/flag_test.go
@@ -4,9 +4,10 @@
"fmt"
"os"
"reflect"
+ "runtime"
"strings"
"testing"
- "runtime"
+ "time"
)
var boolFlagTests = []struct {
@@ -18,36 +19,37 @@
}
func TestBoolFlagHelpOutput(t *testing.T) {
-
for _, test := range boolFlagTests {
flag := BoolFlag{Name: test.name}
output := flag.String()
if output != test.expected {
- t.Errorf("%s does not match %s", output, test.expected)
+ t.Errorf("%q does not match %q", output, test.expected)
}
}
}
var stringFlagTests = []struct {
name string
+ usage string
value string
expected string
}{
- {"help", "", "--help \t"},
- {"h", "", "-h \t"},
- {"h", "", "-h \t"},
- {"test", "Something", "--test \"Something\"\t"},
+ {"foo", "", "", "--foo value\t"},
+ {"f", "", "", "-f value\t"},
+ {"f", "The total `foo` desired", "all", "-f foo\tThe total foo desired (default: \"all\")"},
+ {"test", "", "Something", "--test value\t(default: \"Something\")"},
+ {"config,c", "Load configuration from `FILE`", "", "--config FILE, -c FILE\tLoad configuration from FILE"},
+ {"config,c", "Load configuration from `CONFIG`", "config.json", "--config CONFIG, -c CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"},
}
func TestStringFlagHelpOutput(t *testing.T) {
-
for _, test := range stringFlagTests {
- flag := StringFlag{Name: test.name, Value: test.value}
+ flag := StringFlag{Name: test.name, Usage: test.usage, Value: test.value}
output := flag.String()
if output != test.expected {
- t.Errorf("%s does not match %s", output, test.expected)
+ t.Errorf("%q does not match %q", output, test.expected)
}
}
}
@@ -64,7 +66,7 @@
expectedSuffix = " [%APP_FOO%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
- t.Errorf("%s does not end with" + expectedSuffix, output)
+ t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
@@ -74,30 +76,29 @@
value *StringSlice
expected string
}{
- {"help", func() *StringSlice {
+ {"foo", func() *StringSlice {
s := &StringSlice{}
s.Set("")
return s
- }(), "--help [--help option --help option]\t"},
- {"h", func() *StringSlice {
+ }(), "--foo value\t"},
+ {"f", func() *StringSlice {
s := &StringSlice{}
s.Set("")
return s
- }(), "-h [-h option -h option]\t"},
- {"h", func() *StringSlice {
+ }(), "-f value\t"},
+ {"f", func() *StringSlice {
s := &StringSlice{}
- s.Set("")
+ s.Set("Lipstick")
return s
- }(), "-h [-h option -h option]\t"},
+ }(), "-f value\t(default: \"Lipstick\")"},
{"test", func() *StringSlice {
s := &StringSlice{}
s.Set("Something")
return s
- }(), "--test [--test option --test option]\t"},
+ }(), "--test value\t(default: \"Something\")"},
}
func TestStringSliceFlagHelpOutput(t *testing.T) {
-
for _, test := range stringSliceFlagTests {
flag := StringSliceFlag{Name: test.name, Value: test.value}
output := flag.String()
@@ -120,7 +121,7 @@
expectedSuffix = " [%APP_QWWX%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
- t.Errorf("%q does not end with" + expectedSuffix, output)
+ t.Errorf("%q does not end with"+expectedSuffix, output)
}
}
}
@@ -129,14 +130,13 @@
name string
expected string
}{
- {"help", "--help \"0\"\t"},
- {"h", "-h \"0\"\t"},
+ {"hats", "--hats value\t(default: 9)"},
+ {"H", "-H value\t(default: 9)"},
}
func TestIntFlagHelpOutput(t *testing.T) {
-
for _, test := range intFlagTests {
- flag := IntFlag{Name: test.name}
+ flag := IntFlag{Name: test.name, Value: 9}
output := flag.String()
if output != test.expected {
@@ -157,7 +157,115 @@
expectedSuffix = " [%APP_BAR%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
- t.Errorf("%s does not end with" + expectedSuffix, output)
+ t.Errorf("%s does not end with"+expectedSuffix, output)
+ }
+ }
+}
+
+var int64FlagTests = []struct {
+ name string
+ expected string
+}{
+ {"hats", "--hats value\t(default: 8589934592)"},
+ {"H", "-H value\t(default: 8589934592)"},
+}
+
+func TestInt64FlagHelpOutput(t *testing.T) {
+ for _, test := range int64FlagTests {
+ flag := Int64Flag{Name: test.name, Value: 8589934592}
+ output := flag.String()
+
+ if output != test.expected {
+ t.Errorf("%s does not match %s", output, test.expected)
+ }
+ }
+}
+
+func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) {
+ os.Clearenv()
+ os.Setenv("APP_BAR", "2")
+ for _, test := range int64FlagTests {
+ flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"}
+ output := flag.String()
+
+ expectedSuffix := " [$APP_BAR]"
+ if runtime.GOOS == "windows" {
+ expectedSuffix = " [%APP_BAR%]"
+ }
+ if !strings.HasSuffix(output, expectedSuffix) {
+ t.Errorf("%s does not end with"+expectedSuffix, output)
+ }
+ }
+}
+
+var uintFlagTests = []struct {
+ name string
+ expected string
+}{
+ {"nerfs", "--nerfs value\t(default: 41)"},
+ {"N", "-N value\t(default: 41)"},
+}
+
+func TestUintFlagHelpOutput(t *testing.T) {
+ for _, test := range uintFlagTests {
+ flag := UintFlag{Name: test.name, Value: 41}
+ output := flag.String()
+
+ if output != test.expected {
+ t.Errorf("%s does not match %s", output, test.expected)
+ }
+ }
+}
+
+func TestUintFlagWithEnvVarHelpOutput(t *testing.T) {
+ os.Clearenv()
+ os.Setenv("APP_BAR", "2")
+ for _, test := range uintFlagTests {
+ flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"}
+ output := flag.String()
+
+ expectedSuffix := " [$APP_BAR]"
+ if runtime.GOOS == "windows" {
+ expectedSuffix = " [%APP_BAR%]"
+ }
+ if !strings.HasSuffix(output, expectedSuffix) {
+ t.Errorf("%s does not end with"+expectedSuffix, output)
+ }
+ }
+}
+
+var uint64FlagTests = []struct {
+ name string
+ expected string
+}{
+ {"gerfs", "--gerfs value\t(default: 8589934582)"},
+ {"G", "-G value\t(default: 8589934582)"},
+}
+
+func TestUint64FlagHelpOutput(t *testing.T) {
+ for _, test := range uint64FlagTests {
+ flag := Uint64Flag{Name: test.name, Value: 8589934582}
+ output := flag.String()
+
+ if output != test.expected {
+ t.Errorf("%s does not match %s", output, test.expected)
+ }
+ }
+}
+
+func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) {
+ os.Clearenv()
+ os.Setenv("APP_BAR", "2")
+ for _, test := range uint64FlagTests {
+ flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"}
+ output := flag.String()
+
+ expectedSuffix := " [$APP_BAR]"
+ if runtime.GOOS == "windows" {
+ expectedSuffix = " [%APP_BAR%]"
+ }
+ if !strings.HasSuffix(output, expectedSuffix) {
+ t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
@@ -166,18 +274,17 @@
name string
expected string
}{
- {"help", "--help \"0\"\t"},
- {"h", "-h \"0\"\t"},
+ {"hooting", "--hooting value\t(default: 1s)"},
+ {"H", "-H value\t(default: 1s)"},
}
func TestDurationFlagHelpOutput(t *testing.T) {
-
for _, test := range durationFlagTests {
- flag := DurationFlag{Name: test.name}
+ flag := DurationFlag{Name: test.name, Value: 1 * time.Second}
output := flag.String()
if output != test.expected {
- t.Errorf("%s does not match %s", output, test.expected)
+ t.Errorf("%q does not match %q", output, test.expected)
}
}
}
@@ -194,7 +301,7 @@
expectedSuffix = " [%APP_BAR%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
- t.Errorf("%s does not end with" + expectedSuffix, output)
+ t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
@@ -204,18 +311,17 @@
value *IntSlice
expected string
}{
- {"help", &IntSlice{}, "--help [--help option --help option]\t"},
- {"h", &IntSlice{}, "-h [-h option -h option]\t"},
- {"h", &IntSlice{}, "-h [-h option -h option]\t"},
- {"test", func() *IntSlice {
+ {"heads", &IntSlice{}, "--heads value\t"},
+ {"H", &IntSlice{}, "-H value\t"},
+ {"H, heads", func() *IntSlice {
i := &IntSlice{}
i.Set("9")
+ i.Set("3")
return i
- }(), "--test [--test option --test option]\t"},
+ }(), "-H value, --heads value\t(default: 9, 3)"},
}
func TestIntSliceFlagHelpOutput(t *testing.T) {
-
for _, test := range intSliceFlagTests {
flag := IntSliceFlag{Name: test.name, Value: test.value}
output := flag.String()
@@ -238,7 +344,50 @@
expectedSuffix = " [%APP_SMURF%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
- t.Errorf("%q does not end with" + expectedSuffix, output)
+ t.Errorf("%q does not end with"+expectedSuffix, output)
+ }
+ }
+}
+
+var int64SliceFlagTests = []struct {
+ name string
+ value *Int64Slice
+ expected string
+}{
+ {"heads", &Int64Slice{}, "--heads value\t"},
+ {"H", &Int64Slice{}, "-H value\t"},
+ {"H, heads", func() *Int64Slice {
+ i := &Int64Slice{}
+ i.Set("2")
+ i.Set("17179869184")
+ return i
+ }(), "-H value, --heads value\t(default: 2, 17179869184)"},
+}
+
+func TestInt64SliceFlagHelpOutput(t *testing.T) {
+ for _, test := range int64SliceFlagTests {
+ flag := Int64SliceFlag{Name: test.name, Value: test.value}
+ output := flag.String()
+
+ if output != test.expected {
+ t.Errorf("%q does not match %q", output, test.expected)
+ }
+ }
+}
+
+func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
+ os.Clearenv()
+ os.Setenv("APP_SMURF", "42,17179869184")
+ for _, test := range int64SliceFlagTests {
+ flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"}
+ output := flag.String()
+
+ expectedSuffix := " [$APP_SMURF]"
+ if runtime.GOOS == "windows" {
+ expectedSuffix = " [%APP_SMURF%]"
+ }
+ if !strings.HasSuffix(output, expectedSuffix) {
+ t.Errorf("%q does not end with"+expectedSuffix, output)
}
}
}
@@ -247,18 +396,17 @@
name string
expected string
}{
- {"help", "--help \"0\"\t"},
- {"h", "-h \"0\"\t"},
+ {"hooting", "--hooting value\t(default: 0.1)"},
+ {"H", "-H value\t(default: 0.1)"},
}
func TestFloat64FlagHelpOutput(t *testing.T) {
-
for _, test := range float64FlagTests {
- flag := Float64Flag{Name: test.name}
+ flag := Float64Flag{Name: test.name, Value: float64(0.1)}
output := flag.String()
if output != test.expected {
- t.Errorf("%s does not match %s", output, test.expected)
+ t.Errorf("%q does not match %q", output, test.expected)
}
}
}
@@ -275,7 +423,7 @@
expectedSuffix = " [%APP_BAZ%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
- t.Errorf("%s does not end with" + expectedSuffix, output)
+ t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
@@ -285,12 +433,11 @@
value Generic
expected string
}{
- {"test", &Parser{"abc", "def"}, "--test \"abc,def\"\ttest flag"},
- {"t", &Parser{"abc", "def"}, "-t \"abc,def\"\ttest flag"},
+ {"toads", &Parser{"abc", "def"}, "--toads value\ttest flag (default: abc,def)"},
+ {"t", &Parser{"abc", "def"}, "-t value\ttest flag (default: abc,def)"},
}
func TestGenericFlagHelpOutput(t *testing.T) {
-
for _, test := range genericFlagTests {
flag := GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"}
output := flag.String()
@@ -313,7 +460,7 @@
expectedSuffix = " [%APP_ZAP%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
- t.Errorf("%s does not end with" + expectedSuffix, output)
+ t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
@@ -323,13 +470,14 @@
Flags: []Flag{
StringFlag{Name: "serve, s"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.String("serve") != "10" {
t.Errorf("main name not set")
}
if ctx.String("s") != "10" {
t.Errorf("short name not set")
}
+ return nil
},
}).Run([]string{"run", "-s", "10"})
}
@@ -343,10 +491,11 @@
Destination: &dest,
},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if dest != "10" {
t.Errorf("expected destination String 10")
}
+ return nil
},
}
a.Run([]string{"run", "--dest", "10"})
@@ -359,13 +508,14 @@
Flags: []Flag{
StringFlag{Name: "count, c", EnvVar: "APP_COUNT"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.String("count") != "20" {
t.Errorf("main name not set")
}
if ctx.String("c") != "20" {
t.Errorf("short name not set")
}
+ return nil
},
}).Run([]string{"run"})
}
@@ -377,13 +527,14 @@
Flags: []Flag{
StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.String("count") != "20" {
t.Errorf("main name not set")
}
if ctx.String("c") != "20" {
t.Errorf("short name not set")
}
+ return nil
},
}).Run([]string{"run"})
}
@@ -393,13 +544,14 @@
Flags: []Flag{
StringSliceFlag{Name: "serve, s", Value: &StringSlice{}},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) {
t.Errorf("short name not set")
}
+ return nil
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
@@ -412,13 +564,14 @@
Flags: []Flag{
StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "APP_INTERVALS"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
+ return nil
},
}).Run([]string{"run"})
}
@@ -431,13 +584,14 @@
Flags: []Flag{
StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
+ return nil
},
}).Run([]string{"run"})
}
@@ -447,13 +601,14 @@
Flags: []Flag{
IntFlag{Name: "serve, s"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Int("serve") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("s") != 10 {
t.Errorf("short name not set")
}
+ return nil
},
}
a.Run([]string{"run", "-s", "10"})
@@ -468,10 +623,11 @@
Destination: &dest,
},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if dest != 10 {
t.Errorf("expected destination Int 10")
}
+ return nil
},
}
a.Run([]string{"run", "--dest", "10"})
@@ -484,13 +640,14 @@
Flags: []Flag{
IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Int("timeout") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("t") != 10 {
t.Errorf("short name not set")
}
+ return nil
},
}
a.Run([]string{"run"})
@@ -503,13 +660,14 @@
Flags: []Flag{
IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Int("timeout") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("t") != 10 {
t.Errorf("short name not set")
}
+ return nil
},
}
a.Run([]string{"run"})
@@ -520,13 +678,14 @@
Flags: []Flag{
IntSliceFlag{Name: "serve, s", Value: &IntSlice{}},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
t.Errorf("short name not set")
}
+ return nil
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
@@ -539,13 +698,14 @@
Flags: []Flag{
IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "APP_INTERVALS"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env")
}
+ return nil
},
}).Run([]string{"run"})
}
@@ -558,13 +718,71 @@
Flags: []Flag{
IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env")
}
+ return nil
+ },
+ }).Run([]string{"run"})
+}
+
+func TestParseMultiInt64Slice(t *testing.T) {
+ (&App{
+ Flags: []Flag{
+ Int64SliceFlag{Name: "serve, s", Value: &Int64Slice{}},
+ },
+ Action: func(ctx *Context) error {
+ if !reflect.DeepEqual(ctx.Int64Slice("serve"), []int64{10, 17179869184}) {
+ t.Errorf("main name not set")
+ }
+ if !reflect.DeepEqual(ctx.Int64Slice("s"), []int64{10, 17179869184}) {
+ t.Errorf("short name not set")
+ }
+ return nil
+ },
+ }).Run([]string{"run", "-s", "10", "-s", "17179869184"})
+}
+
+func TestParseMultiInt64SliceFromEnv(t *testing.T) {
+ os.Clearenv()
+ os.Setenv("APP_INTERVALS", "20,30,17179869184")
+
+ (&App{
+ Flags: []Flag{
+ Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "APP_INTERVALS"},
+ },
+ Action: func(ctx *Context) error {
+ if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) {
+ t.Errorf("main name not set from env")
+ }
+ if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) {
+ t.Errorf("short name not set from env")
+ }
+ return nil
+ },
+ }).Run([]string{"run"})
+}
+
+func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) {
+ os.Clearenv()
+ os.Setenv("APP_INTERVALS", "20,30,17179869184")
+
+ (&App{
+ Flags: []Flag{
+ Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
+ },
+ Action: func(ctx *Context) error {
+ if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) {
+ t.Errorf("main name not set from env")
+ }
+ if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) {
+ t.Errorf("short name not set from env")
+ }
+ return nil
},
}).Run([]string{"run"})
}
@@ -574,13 +792,14 @@
Flags: []Flag{
Float64Flag{Name: "serve, s"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Float64("serve") != 10.2 {
t.Errorf("main name not set")
}
if ctx.Float64("s") != 10.2 {
t.Errorf("short name not set")
}
+ return nil
},
}
a.Run([]string{"run", "-s", "10.2"})
@@ -595,10 +814,11 @@
Destination: &dest,
},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if dest != 10.2 {
t.Errorf("expected destination Float64 10.2")
}
+ return nil
},
}
a.Run([]string{"run", "--dest", "10.2"})
@@ -611,13 +831,14 @@
Flags: []Flag{
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Float64("timeout") != 15.5 {
t.Errorf("main name not set")
}
if ctx.Float64("t") != 15.5 {
t.Errorf("short name not set")
}
+ return nil
},
}
a.Run([]string{"run"})
@@ -630,13 +851,14 @@
Flags: []Flag{
Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Float64("timeout") != 15.5 {
t.Errorf("main name not set")
}
if ctx.Float64("t") != 15.5 {
t.Errorf("short name not set")
}
+ return nil
},
}
a.Run([]string{"run"})
@@ -647,13 +869,14 @@
Flags: []Flag{
BoolFlag{Name: "serve, s"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Bool("serve") != true {
t.Errorf("main name not set")
}
if ctx.Bool("s") != true {
t.Errorf("short name not set")
}
+ return nil
},
}
a.Run([]string{"run", "--serve"})
@@ -668,10 +891,11 @@
Destination: &dest,
},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if dest != true {
t.Errorf("expected destination Bool true")
}
+ return nil
},
}
a.Run([]string{"run", "--dest"})
@@ -684,13 +908,14 @@
Flags: []Flag{
BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Bool("debug") != true {
t.Errorf("main name not set from env")
}
if ctx.Bool("d") != true {
t.Errorf("short name not set from env")
}
+ return nil
},
}
a.Run([]string{"run"})
@@ -703,13 +928,14 @@
Flags: []Flag{
BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Bool("debug") != true {
t.Errorf("main name not set from env")
}
if ctx.Bool("d") != true {
t.Errorf("short name not set from env")
}
+ return nil
},
}
a.Run([]string{"run"})
@@ -720,13 +946,14 @@
Flags: []Flag{
BoolTFlag{Name: "serve, s"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.BoolT("serve") != true {
t.Errorf("main name not set")
}
if ctx.BoolT("s") != true {
t.Errorf("short name not set")
}
+ return nil
},
}
a.Run([]string{"run", "--serve"})
@@ -741,10 +968,11 @@
Destination: &dest,
},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if dest != true {
t.Errorf("expected destination BoolT true")
}
+ return nil
},
}
a.Run([]string{"run", "--dest"})
@@ -757,13 +985,14 @@
Flags: []Flag{
BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.BoolT("debug") != false {
t.Errorf("main name not set from env")
}
if ctx.BoolT("d") != false {
t.Errorf("short name not set from env")
}
+ return nil
},
}
a.Run([]string{"run"})
@@ -776,13 +1005,14 @@
Flags: []Flag{
BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.BoolT("debug") != false {
t.Errorf("main name not set from env")
}
if ctx.BoolT("d") != false {
t.Errorf("short name not set from env")
}
+ return nil
},
}
a.Run([]string{"run"})
@@ -811,13 +1041,14 @@
Flags: []Flag{
GenericFlag{Name: "serve, s", Value: &Parser{}},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) {
t.Errorf("short name not set")
}
+ return nil
},
}
a.Run([]string{"run", "-s", "10,20"})
@@ -830,13 +1061,14 @@
Flags: []Flag{
GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) {
t.Errorf("short name not set from env")
}
+ return nil
},
}
a.Run([]string{"run"})
@@ -849,10 +1081,11 @@
Flags: []Flag{
GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) {
t.Errorf("value not set from env")
}
+ return nil
},
}
a.Run([]string{"run"})
diff --git a/vendor/github.com/codegangsta/cli/funcs.go b/vendor/github.com/codegangsta/cli/funcs.go
new file mode 100644
index 0000000..cba5e6c
--- /dev/null
+++ b/vendor/github.com/codegangsta/cli/funcs.go
@@ -0,0 +1,28 @@
+package cli
+
+// BashCompleteFunc is an action to execute when the bash-completion flag is set
+type BashCompleteFunc func(*Context)
+
+// BeforeFunc is an action to execute before any subcommands are run, but after
+// the context is ready if a non-nil error is returned, no subcommands are run
+type BeforeFunc func(*Context) error
+
+// AfterFunc is an action to execute after any subcommands are run, but after the
+// subcommand has finished it is run even if Action() panics
+type AfterFunc func(*Context) error
+
+// ActionFunc is the action to execute when no subcommands are specified
+type ActionFunc func(*Context) error
+
+// CommandNotFoundFunc is executed if the proper command cannot be found
+type CommandNotFoundFunc func(*Context, string)
+
+// OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying
+// customized usage error messages. This function is able to replace the
+// original error messages. If this function is not set, the "Incorrect usage"
+// is displayed and the execution is interrupted.
+type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error
+
+// FlagStringFunc is used by the help generation to display a flag, which is
+// expected to be a single line.
+type FlagStringFunc func(Flag) string
diff --git a/vendor/github.com/codegangsta/cli/help.go b/vendor/github.com/codegangsta/cli/help.go
index adf157d..0f4cf14 100644
--- a/vendor/github.com/codegangsta/cli/help.go
+++ b/vendor/github.com/codegangsta/cli/help.go
@@ -3,73 +3,74 @@
import (
"fmt"
"io"
+ "os"
"strings"
"text/tabwriter"
"text/template"
)
-// The text template for the Default help topic.
+// AppHelpTemplate is the text template for the Default help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var AppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}
USAGE:
- {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
+ {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
{{if .Version}}{{if not .HideVersion}}
VERSION:
{{.Version}}
{{end}}{{end}}{{if len .Authors}}
AUTHOR(S):
- {{range .Authors}}{{ . }}{{end}}
- {{end}}{{if .Commands}}
-COMMANDS:{{range .Categories}}{{if .Name}}
- {{.Name}}{{ ":" }}{{end}}{{range .Commands}}
- {{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}{{end}}
-{{end}}{{end}}{{if .Flags}}
+ {{range .Authors}}{{.}}{{end}}
+ {{end}}{{if .VisibleCommands}}
+COMMANDS:{{range .VisibleCategories}}{{if .Name}}
+ {{.Name}}:{{end}}{{range .VisibleCommands}}
+ {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
+{{end}}{{end}}{{if .VisibleFlags}}
GLOBAL OPTIONS:
- {{range .Flags}}{{.}}
- {{end}}{{end}}{{if .Copyright }}
+ {{range .VisibleFlags}}{{.}}
+ {{end}}{{end}}{{if .Copyright}}
COPYRIGHT:
{{.Copyright}}
{{end}}
`
-// The text template for the command help topic.
+// CommandHelpTemplate is the text template for the command help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var CommandHelpTemplate = `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
- {{.HelpName}}{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}}
+ {{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}}
CATEGORY:
{{.Category}}{{end}}{{if .Description}}
DESCRIPTION:
- {{.Description}}{{end}}{{if .Flags}}
+ {{.Description}}{{end}}{{if .VisibleFlags}}
OPTIONS:
- {{range .Flags}}{{.}}
- {{end}}{{ end }}
+ {{range .VisibleFlags}}{{.}}
+ {{end}}{{end}}
`
-// The text template for the subcommand help topic.
+// SubcommandHelpTemplate is the text template for the subcommand help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var SubcommandHelpTemplate = `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
- {{.HelpName}} command{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
+ {{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
-COMMANDS:{{range .Categories}}{{if .Name}}
- {{.Name}}{{ ":" }}{{end}}{{range .Commands}}
- {{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}{{end}}
-{{end}}{{if .Flags}}
+COMMANDS:{{range .VisibleCategories}}{{if .Name}}
+ {{.Name}}:{{end}}{{range .VisibleCommands}}
+ {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
+{{end}}{{if .VisibleFlags}}
OPTIONS:
- {{range .Flags}}{{.}}
+ {{range .VisibleFlags}}{{.}}
{{end}}{{end}}
`
@@ -78,13 +79,14 @@
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
- Action: func(c *Context) {
+ Action: func(c *Context) error {
args := c.Args()
if args.Present() {
- ShowCommandHelp(c, args.First())
- } else {
- ShowAppHelp(c)
+ return ShowCommandHelp(c, args.First())
}
+
+ ShowAppHelp(c)
+ return nil
},
}
@@ -93,65 +95,74 @@
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
- Action: func(c *Context) {
+ Action: func(c *Context) error {
args := c.Args()
if args.Present() {
- ShowCommandHelp(c, args.First())
- } else {
- ShowSubcommandHelp(c)
+ return ShowCommandHelp(c, args.First())
}
+
+ return ShowSubcommandHelp(c)
},
}
// Prints help for the App or Command
type helpPrinter func(w io.Writer, templ string, data interface{})
+// HelpPrinter is a function that writes the help output. If not set a default
+// is used. The function signature is:
+// func(w io.Writer, templ string, data interface{})
var HelpPrinter helpPrinter = printHelp
-// Prints version for the App
+// VersionPrinter prints the version for the App
var VersionPrinter = printVersion
-func ShowAppHelp(c *Context) {
+// ShowAppHelp is an action that displays the help.
+func ShowAppHelp(c *Context) error {
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
+ return nil
}
-// Prints the list of subcommands as the default app completion method
+// DefaultAppComplete prints the list of subcommands as the default app completion method
func DefaultAppComplete(c *Context) {
for _, command := range c.App.Commands {
+ if command.Hidden {
+ continue
+ }
for _, name := range command.Names() {
fmt.Fprintln(c.App.Writer, name)
}
}
}
-// Prints help for the given command
-func ShowCommandHelp(ctx *Context, command string) {
+// ShowCommandHelp prints help for the given command
+func ShowCommandHelp(ctx *Context, command string) error {
// show the subcommand help for a command with subcommands
if command == "" {
HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
- return
+ return nil
}
for _, c := range ctx.App.Commands {
if c.HasName(command) {
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
- return
+ return nil
}
}
- if ctx.App.CommandNotFound != nil {
- ctx.App.CommandNotFound(ctx, command)
- } else {
- fmt.Fprintf(ctx.App.Writer, "No help topic for '%v'\n", command)
+ if ctx.App.CommandNotFound == nil {
+ return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3)
}
+
+ ctx.App.CommandNotFound(ctx, command)
+ return nil
}
-// Prints help for the given subcommand
-func ShowSubcommandHelp(c *Context) {
- ShowCommandHelp(c, c.Command.Name)
+// ShowSubcommandHelp prints help for the given subcommand
+func ShowSubcommandHelp(c *Context) error {
+ return ShowCommandHelp(c, c.Command.Name)
}
-// Prints the version number of the App
+// ShowVersion prints the version number of the App
func ShowVersion(c *Context) {
VersionPrinter(c)
}
@@ -160,7 +171,7 @@
fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
}
-// Prints the lists of commands within a given context
+// ShowCompletions prints the lists of commands within a given context
func ShowCompletions(c *Context) {
a := c.App
if a != nil && a.BashComplete != nil {
@@ -168,7 +179,7 @@
}
}
-// Prints the custom completions for a given command
+// ShowCommandCompletions prints the custom completions for a given command
func ShowCommandCompletions(ctx *Context, command string) {
c := ctx.App.Command(command)
if c != nil && c.BashComplete != nil {
@@ -181,12 +192,15 @@
"join": strings.Join,
}
- w := tabwriter.NewWriter(out, 0, 8, 1, '\t', 0)
+ w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
err := t.Execute(w, data)
if err != nil {
// If the writer is closed, t.Execute will fail, and there's nothing
- // we can do to recover. We could send this to os.Stderr if we need.
+ // we can do to recover.
+ if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" {
+ fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err)
+ }
return
}
w.Flush()
diff --git a/vendor/github.com/codegangsta/cli/help_test.go b/vendor/github.com/codegangsta/cli/help_test.go
index 0821f48..7c15400 100644
--- a/vendor/github.com/codegangsta/cli/help_test.go
+++ b/vendor/github.com/codegangsta/cli/help_test.go
@@ -2,6 +2,8 @@
import (
"bytes"
+ "flag"
+ "strings"
"testing"
)
@@ -66,10 +68,11 @@
Flags: []Flag{
BoolFlag{Name: "foo, h"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Bool("h") != true {
t.Errorf("custom help flag not set")
}
+ return nil
},
}
output := new(bytes.Buffer)
@@ -95,10 +98,11 @@
Flags: []Flag{
BoolFlag{Name: "foo, v"},
},
- Action: func(ctx *Context) {
+ Action: func(ctx *Context) error {
if ctx.Bool("v") != true {
t.Errorf("custom version flag not set")
}
+ return nil
},
}
output := new(bytes.Buffer)
@@ -108,3 +112,178 @@
t.Errorf("unexpected output: %s", output.String())
}
}
+
+func Test_helpCommand_Action_ErrorIfNoTopic(t *testing.T) {
+ app := NewApp()
+
+ set := flag.NewFlagSet("test", 0)
+ set.Parse([]string{"foo"})
+
+ c := NewContext(app, set, nil)
+
+ err := helpCommand.Action.(func(*Context) error)(c)
+
+ if err == nil {
+ t.Fatalf("expected error from helpCommand.Action(), but got nil")
+ }
+
+ exitErr, ok := err.(*ExitError)
+ if !ok {
+ t.Fatalf("expected ExitError from helpCommand.Action(), but instead got: %v", err.Error())
+ }
+
+ if !strings.HasPrefix(exitErr.Error(), "No help topic for") {
+ t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error())
+ }
+
+ if exitErr.exitCode != 3 {
+ t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode)
+ }
+}
+
+func Test_helpCommand_InHelpOutput(t *testing.T) {
+ app := NewApp()
+ output := &bytes.Buffer{}
+ app.Writer = output
+ app.Run([]string{"test", "--help"})
+
+ s := output.String()
+
+ if strings.Contains(s, "\nCOMMANDS:\nGLOBAL OPTIONS:\n") {
+ t.Fatalf("empty COMMANDS section detected: %q", s)
+ }
+
+ if !strings.Contains(s, "help, h") {
+ t.Fatalf("missing \"help, h\": %q", s)
+ }
+}
+
+func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) {
+ app := NewApp()
+
+ set := flag.NewFlagSet("test", 0)
+ set.Parse([]string{"foo"})
+
+ c := NewContext(app, set, nil)
+
+ err := helpSubcommand.Action.(func(*Context) error)(c)
+
+ if err == nil {
+ t.Fatalf("expected error from helpCommand.Action(), but got nil")
+ }
+
+ exitErr, ok := err.(*ExitError)
+ if !ok {
+ t.Fatalf("expected ExitError from helpCommand.Action(), but instead got: %v", err.Error())
+ }
+
+ if !strings.HasPrefix(exitErr.Error(), "No help topic for") {
+ t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error())
+ }
+
+ if exitErr.exitCode != 3 {
+ t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode)
+ }
+}
+
+func TestShowAppHelp_CommandAliases(t *testing.T) {
+ app := &App{
+ Commands: []Command{
+ {
+ Name: "frobbly",
+ Aliases: []string{"fr", "frob"},
+ Action: func(ctx *Context) error {
+ return nil
+ },
+ },
+ },
+ }
+
+ output := &bytes.Buffer{}
+ app.Writer = output
+ app.Run([]string{"foo", "--help"})
+
+ if !strings.Contains(output.String(), "frobbly, fr, frob") {
+ t.Errorf("expected output to include all command aliases; got: %q", output.String())
+ }
+}
+
+func TestShowCommandHelp_CommandAliases(t *testing.T) {
+ app := &App{
+ Commands: []Command{
+ {
+ Name: "frobbly",
+ Aliases: []string{"fr", "frob", "bork"},
+ Action: func(ctx *Context) error {
+ return nil
+ },
+ },
+ },
+ }
+
+ output := &bytes.Buffer{}
+ app.Writer = output
+ app.Run([]string{"foo", "help", "fr"})
+
+ if !strings.Contains(output.String(), "frobbly") {
+ t.Errorf("expected output to include command name; got: %q", output.String())
+ }
+
+ if strings.Contains(output.String(), "bork") {
+ t.Errorf("expected output to exclude command aliases; got: %q", output.String())
+ }
+}
+
+func TestShowSubcommandHelp_CommandAliases(t *testing.T) {
+ app := &App{
+ Commands: []Command{
+ {
+ Name: "frobbly",
+ Aliases: []string{"fr", "frob", "bork"},
+ Action: func(ctx *Context) error {
+ return nil
+ },
+ },
+ },
+ }
+
+ output := &bytes.Buffer{}
+ app.Writer = output
+ app.Run([]string{"foo", "help"})
+
+ if !strings.Contains(output.String(), "frobbly, fr, frob, bork") {
+ t.Errorf("expected output to include all command aliases; got: %q", output.String())
+ }
+}
+
+func TestShowAppHelp_HiddenCommand(t *testing.T) {
+ app := &App{
+ Commands: []Command{
+ {
+ Name: "frobbly",
+ Action: func(ctx *Context) error {
+ return nil
+ },
+ },
+ {
+ Name: "secretfrob",
+ Hidden: true,
+ Action: func(ctx *Context) error {
+ return nil
+ },
+ },
+ },
+ }
+
+ output := &bytes.Buffer{}
+ app.Writer = output
+ app.Run([]string{"app", "--help"})
+
+ if strings.Contains(output.String(), "secretfrob") {
+ t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String())
+ }
+
+ if !strings.Contains(output.String(), "frobbly") {
+ t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
+ }
+}
diff --git a/vendor/github.com/codegangsta/cli/helpers_test.go b/vendor/github.com/codegangsta/cli/helpers_test.go
index b1b7339..109ea7a 100644
--- a/vendor/github.com/codegangsta/cli/helpers_test.go
+++ b/vendor/github.com/codegangsta/cli/helpers_test.go
@@ -1,14 +1,23 @@
package cli
import (
+ "os"
"reflect"
+ "runtime"
+ "strings"
"testing"
)
-/* Test Helpers */
+var (
+ wd, _ = os.Getwd()
+)
+
func expect(t *testing.T, a interface{}, b interface{}) {
+ _, fn, line, _ := runtime.Caller(1)
+ fn = strings.Replace(fn, wd+"/", "", -1)
+
if !reflect.DeepEqual(a, b) {
- t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
+ t.Errorf("(%s:%d) Expected %v (type %v) - Got %v (type %v)", fn, line, b, reflect.TypeOf(b), a, reflect.TypeOf(a))
}
}
diff --git a/vendor/github.com/codegangsta/cli/runtests b/vendor/github.com/codegangsta/cli/runtests
new file mode 100755
index 0000000..0a7b483
--- /dev/null
+++ b/vendor/github.com/codegangsta/cli/runtests
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+from __future__ import print_function
+
+import argparse
+import os
+import sys
+import tempfile
+
+from subprocess import check_call, check_output
+
+
+PACKAGE_NAME = os.environ.get(
+ 'CLI_PACKAGE_NAME', 'github.com/urfave/cli'
+)
+
+
+def main(sysargs=sys.argv[:]):
+ targets = {
+ 'vet': _vet,
+ 'test': _test,
+ 'gfmxr': _gfmxr,
+ 'toc': _toc,
+ }
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ 'target', nargs='?', choices=tuple(targets.keys()), default='test'
+ )
+ args = parser.parse_args(sysargs[1:])
+
+ targets[args.target]()
+ return 0
+
+
+def _test():
+ if check_output('go version'.split()).split()[2] < 'go1.2':
+ _run('go test -v .'.split())
+ return
+
+ coverprofiles = []
+ for subpackage in ['', 'altsrc']:
+ coverprofile = 'cli.coverprofile'
+ if subpackage != '':
+ coverprofile = '{}.coverprofile'.format(subpackage)
+
+ coverprofiles.append(coverprofile)
+
+ _run('go test -v'.split() + [
+ '-coverprofile={}'.format(coverprofile),
+ ('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/')
+ ])
+
+ combined_name = _combine_coverprofiles(coverprofiles)
+ _run('go tool cover -func={}'.format(combined_name).split())
+ os.remove(combined_name)
+
+
+def _gfmxr():
+ _run(['gfmxr', '-c', str(_gfmxr_count()), '-s', 'README.md'])
+
+
+def _vet():
+ _run('go vet ./...'.split())
+
+
+def _toc():
+ _run(['node_modules/.bin/markdown-toc', '-i', 'README.md'])
+ _run(['git', 'diff', '--quiet'])
+
+
+def _run(command):
+ print('runtests: {}'.format(' '.join(command)), file=sys.stderr)
+ check_call(command)
+
+
+def _gfmxr_count():
+ with open('README.md') as infile:
+ lines = infile.read().splitlines()
+ return len(filter(_is_go_runnable, lines))
+
+
+def _is_go_runnable(line):
+ return line.startswith('package main')
+
+
+def _combine_coverprofiles(coverprofiles):
+ combined = tempfile.NamedTemporaryFile(
+ suffix='.coverprofile', delete=False
+ )
+ combined.write('mode: set\n')
+
+ for coverprofile in coverprofiles:
+ with open(coverprofile, 'r') as infile:
+ for line in infile.readlines():
+ if not line.startswith('mode: '):
+ combined.write(line)
+
+ combined.flush()
+ name = combined.name
+ combined.close()
+ return name
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/vendor/gopkg.in/yaml.v2/LICENSE b/vendor/gopkg.in/yaml.v2/LICENSE
index a68e67f..866d74a 100644
--- a/vendor/gopkg.in/yaml.v2/LICENSE
+++ b/vendor/gopkg.in/yaml.v2/LICENSE
@@ -1,188 +1,13 @@
+Copyright 2011-2016 Canonical Ltd.
-Copyright (c) 2011-2014 - Canonical Inc.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
-This software is licensed under the LGPLv3, included below.
+ http://www.apache.org/licenses/LICENSE-2.0
-As a special exception to the GNU Lesser General Public License version 3
-("LGPL3"), the copyright holders of this Library give you permission to
-convey to a third party a Combined Work that links statically or dynamically
-to this Library without providing any Minimal Corresponding Source or
-Minimal Application Code as set out in 4d or providing the installation
-information set out in section 4e, provided that you comply with the other
-provisions of LGPL3 and provided that you meet, for the Application the
-terms and conditions of the license(s) which apply to the Application.
-
-Except as stated in this special exception, the provisions of LGPL3 will
-continue to comply in full to this Library. If you modify this Library, you
-may apply this exception to your version of this Library, but you are not
-obliged to do so. If you do not wish to do so, delete this exception
-statement from your version. This exception does not (and cannot) modify any
-license terms which apply to the Application, with which you must still
-comply.
-
-
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.