Repository: Masterminds/glide Branch: master Commit: b94b39d657d8 Files: 217 Total size: 1.1 MB Directory structure: gitextract_bnqaxfk6/ ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── action/ │ ├── about.go │ ├── about_test.go │ ├── cache.go │ ├── config_wizard.go │ ├── create.go │ ├── debug.go │ ├── doc.go │ ├── ensure.go │ ├── get.go │ ├── get_test.go │ ├── import_gb.go │ ├── import_godep.go │ ├── import_gom.go │ ├── import_gpm.go │ ├── init.go │ ├── install.go │ ├── list.go │ ├── list_test.go │ ├── mirrors.go │ ├── name.go │ ├── name_test.go │ ├── no_vendor.go │ ├── no_vendor_test.go │ ├── plugin.go │ ├── plugin_test.go │ ├── project_info.go │ ├── rebuild.go │ ├── rebuild_test.go │ ├── remove.go │ ├── tree.go │ └── update.go ├── appveyor.yml ├── cache/ │ ├── cache.go │ ├── cache_test.go │ ├── global_lock.go │ └── memory.go ├── cfg/ │ ├── cfg.go │ ├── config.go │ ├── config_test.go │ ├── lock.go │ └── lock_test.go ├── dependency/ │ ├── resolver.go │ ├── resolver_test.go │ └── scan.go ├── docs/ │ ├── commands.md │ ├── example-glide.yaml │ ├── faq.md │ ├── getting-started.md │ ├── glide-plugin-example │ ├── glide.lock.md │ ├── glide.yaml.md │ ├── importing.md │ ├── index.md │ ├── plugins.md │ ├── resolving-imports.md │ ├── vendor.md │ └── versions.md ├── gb/ │ ├── gb.go │ └── manifest.go ├── glide.go ├── glide.yaml ├── glide_test.go ├── godep/ │ ├── godep.go │ └── strip/ │ ├── strip.go │ └── strip_test.go ├── gom/ │ ├── gom.go │ └── parser.go ├── gpm/ │ └── gpm.go ├── importer/ │ └── importer.go ├── mirrors/ │ ├── cfg.go │ ├── mirrors.go │ └── mirrors_test.go ├── mkdocs.yml ├── msg/ │ ├── msg.go │ ├── out.go │ └── out_windows.go ├── path/ │ ├── path.go │ ├── path_test.go │ ├── strip.go │ ├── strip_int_test.go │ ├── strip_test.go │ └── winbug.go ├── repo/ │ ├── installer.go │ ├── repo.go │ ├── semver.go │ ├── set_reference.go │ ├── tracker.go │ ├── tracker_test.go │ └── vcs.go ├── testdata/ │ ├── name/ │ │ ├── glide.yaml │ │ └── glide2.yaml │ ├── nv/ │ │ ├── a/ │ │ │ └── foo.empty │ │ ├── b/ │ │ │ └── foo.empty │ │ └── c/ │ │ └── foo.empty │ ├── path/ │ │ ├── a/ │ │ │ ├── b/ │ │ │ │ └── c/ │ │ │ │ └── placeholder.empty │ │ │ └── glide.yaml │ │ └── x/ │ │ ├── glide.yaml │ │ ├── symlinked_vendor/ │ │ │ └── placeholder.empty │ │ └── y/ │ │ └── z/ │ │ └── placeholder.empty │ ├── plugin/ │ │ ├── glide-hello │ │ └── glide-hello-win.bat │ └── rebuild/ │ └── glide.yaml ├── tree/ │ ├── tree.go │ └── tree_test.go ├── util/ │ ├── normalizename_test.go │ ├── util.go │ └── util_test.go └── vendor/ ├── github.com/ │ ├── Masterminds/ │ │ ├── semver/ │ │ │ ├── .travis.yml │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE.txt │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── appveyor.yml │ │ │ ├── benchmark_test.go │ │ │ ├── collection.go │ │ │ ├── collection_test.go │ │ │ ├── constraints.go │ │ │ ├── constraints_test.go │ │ │ ├── doc.go │ │ │ ├── version.go │ │ │ └── version_test.go │ │ └── vcs/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CHANGELOG.md │ │ ├── LICENSE.txt │ │ ├── Makefile │ │ ├── README.md │ │ ├── appveyor.yml │ │ ├── bzr.go │ │ ├── bzr_test.go │ │ ├── errors.go │ │ ├── errors_test.go │ │ ├── git.go │ │ ├── git_test.go │ │ ├── glide.yaml │ │ ├── hg.go │ │ ├── hg_test.go │ │ ├── repo.go │ │ ├── repo_test.go │ │ ├── svn.go │ │ ├── svn_test.go │ │ ├── vcs_local_lookup.go │ │ ├── vcs_remote_lookup.go │ │ └── vcs_remote_lookup_test.go │ ├── codegangsta/ │ │ └── cli/ │ │ ├── .flake8 │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── altsrc/ │ │ │ ├── altsrc.go │ │ │ ├── flag.go │ │ │ ├── flag_generated.go │ │ │ ├── flag_test.go │ │ │ ├── helpers_test.go │ │ │ ├── input_source_context.go │ │ │ ├── map_input_source.go │ │ │ ├── toml_command_test.go │ │ │ ├── toml_file_loader.go │ │ │ ├── yaml_command_test.go │ │ │ └── yaml_file_loader.go │ │ ├── app.go │ │ ├── app_test.go │ │ ├── appveyor.yml │ │ ├── autocomplete/ │ │ │ ├── bash_autocomplete │ │ │ └── zsh_autocomplete │ │ ├── category.go │ │ ├── cli.go │ │ ├── command.go │ │ ├── command_test.go │ │ ├── context.go │ │ ├── context_test.go │ │ ├── errors.go │ │ ├── errors_test.go │ │ ├── flag-types.json │ │ ├── flag.go │ │ ├── flag_generated.go │ │ ├── flag_test.go │ │ ├── funcs.go │ │ ├── generate-flag-types │ │ ├── help.go │ │ ├── help_test.go │ │ ├── helpers_test.go │ │ ├── helpers_unix_test.go │ │ ├── helpers_windows_test.go │ │ └── runtests │ └── mitchellh/ │ └── go-homedir/ │ ├── LICENSE │ ├── README.md │ ├── homedir.go │ └── homedir_test.go └── gopkg.in/ └── yaml.v2/ ├── .travis.yml ├── LICENSE ├── LICENSE.libyaml ├── README.md ├── apic.go ├── decode.go ├── decode_test.go ├── emitterc.go ├── encode.go ├── encode_test.go ├── example_embedded_test.go ├── parserc.go ├── readerc.go ├── resolve.go ├── scannerc.go ├── sorter.go ├── suite_test.go ├── writerc.go ├── yaml.go ├── yamlh.go └── yamlprivateh.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ glide glide.exe *.a *.sublime-project *.sublime-workspace dist/ .DS_Store .idea ================================================ FILE: .travis.yml ================================================ language: go go: - 1.7.x - 1.8.x - 1.9.x - 1.10.x - 1.11.x - 1.12.x - tip # Setting sudo access to false will let Travis CI use containers rather than # VMs to run the tests. For more details see: # - http://docs.travis-ci.com/user/workers/container-based-infrastructure/ # - http://docs.travis-ci.com/user/workers/standard-infrastructure/ sudo: false # The default script is go test -v ./... which will test everything # in the vendor directory. We don't need to test all dependent packages. # Only testing this project. script: - make test integration-test verify-version notifications: webhooks: urls: - https://webhooks.gitter.im/e/67e4b42cbf763625d0b4 on_success: change on_failure: always on_start: never ================================================ FILE: CHANGELOG.md ================================================ # Release 0.13.4 (unreleased) # Release 0.13.3 (2019-07-12) ## Fixed - #1056: Fixed issue where Glide is not detecting crypto/ed25519, now in the stdlib (thanks @martinkunc) - #1033: Fixed segfault with Glide 0.13.2 when stripping Godep workspace (thanks @databus23) # Release 0.13.2 (2018-09-26) ## Fixed - #956: Fixed Error handling nested vendor folders (thanks @apynes2) - #953: Fixed issue where error was not propagating # Release 0.13.1 (2017-11-07) ## Fixed - #935: Fix handling of new core package math/bits (thanks @prateek) # Release 0.13.0 (2017-09-28) ## Added - #631: Verify version during build in automation (thanks @breerly) - #711: Added a commit hash example to the docs (thanks @mh-cbon) - #771: Handling default GOPATH for Go 1.8 - #814: Adding install instructions for Ubuntu 17.04 (thanks @HaraldNordgren) - #870: Added support for s390x architecture (thanks @Nayana-ibm) ## Changed - #582: Removed verbose flag as it was not being used (thanks @kelcecil) - #697: Preserve vendor/.git, if it exists. (thanks @sdboyer) - #686: Make ending dots in output more consistent (thanks @stevenroose) - #748: Updated tests to work windows and add windows CI testing - #717: Cache GOROOT at init time for performance (thanks @heyitsanthony) - #797, #821, #908: Updating to the latest version of the dependencies - #800: Allow VERSION of glide to be passed in with build script (thanks @BlackYoup) - #774: Add docs on using go get to install glide (thanks @philoserf) - #907: Updated Travis CI language versions of Go to test against (thanks @dvrkps) - #916: Update gox to version managed by Masterminds for builds ## Fixed - #736: Find home dir without using cgo (thanks @krancour) - #603: Fixed where, in some cases not importing dependencies config - #620: Fixed grammar usage on projects (thanks @server-monitor) - #623: Fixed typos in help and (thanks @jonboulle) - #628: Fixed typos (thanks @philoserf) - #733: Fixed documentation issues (thanks @matiasanaya) - #747: Fixed issue with glide home directory (thanks @agatan) - #759: More spelling fixes (thanks @jbirch) - #775: Even more doc typo fixes (thanks @cristiangreco) - #811: Fixed issue with windows git submodules - #819: Fixed more typos (thanks @zoofood) - #829: Fixed preservation of .git files correctly (@RaduBerinde) - #778: Fixed removing and moving large sets of files fails on Windows - #910: Fixed issue due to go/build.ImportDir change response on not found dir - #906: Fixed CustomRemoveAll() to handle spaces in paths, and also file not found (thanks @jpz) # Release 0.12.3 (2016-10-03) ## Fixed - #615: Fixed possible situation where resolver could get stuck in a loop # Release 0.12.2 (2016-09-13) ## Fixed - #599: In some cases was not importing dependencies config - #601: Fixed issue where --all-dependencies flag stopped working # Release 0.12.1 (2016-08-31) ## Fixed - #578: Not resolving parent project packages in some cases - #580: cross-device error handling failed on Windows in some cases - #590: When exit signal received remove global lock Note, Plan 9 is an experimental OS for Go. Due to some issues we are not going to be supporting builds for it at this time. # Release 0.12.0 (2016-08-23) ## Added - Support for distributions in FreeBSD, OpenBSD, NetBSD, and Plan9 - #528: ARM release support (thanks @franciscocpg) - #563: Added initial integration testing - #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 - #287: When file or directory not found provide useful message - #559: Fixed error is nil issue (thanks @mfycheng) - #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 - #505: Ignored dependency showing up in testImport # Release 0.11.0 (2016-07-05) ## Added - #461: Resolve test imports - #458: Wizard and version detection are now on `glide get` - #444: New config wizard helps you find versions and set ranges. Can be run from `glide init` or as separate command - #438: Added ability to read symlink basedirs (thanks @klnusbaum) - #436: Added .idea to .gitignore - #393 and #401: Added a PPA (https://github.com/Masterminds/glide-ppa) and instructions on using it (thanks @franciscocpg) - #390: Added support for custom Go executable name. Needed for environments like appengine. Environment variable GLIDE_GO_EXECUTABLE (thanks @dpmcnevin) - #382: `glide info` command takes a format string and returns info (thanks @franciscocpg) - #365: glide list: support json output format (thanks @chancez) ## Changed - Tags are now in the form v[SemVer]. The change is the initial v on the tag. This is to conform with other Go tools that require this. - #501: Updating the plugins documentation and adding listing - #500: Log an error if stripping version control data fails (thanks @alexbrand) - #496: Updated to github.com/Masterminds/semver 1.1.1 - #495: Updated to github.com/Masterminds/vcs 1.8.0 - #494: Glide install skips fetch when it is up to date - #489: Make shared funcs for lockfile usage (thanks @heewa) - #459: When a conflict occurs output the tag, if one exists, for the commit - #443: Updating message indentation to be uniform - #431: Updated the docs on subpackages - #433: The global shared cache was reworked in prep for future uses - #396: Don't update the lock file if nothing has changed ## Fixed - #460: Sometimes ignored packages were written to lock file. Fixed. - #463: Fixed possible nil pointer issues - #453: Fix DeleteUnused flag which was not working (thanks @s-urbaniak) - #432: Fixed issue with new net/http/httptrace std lib package - #392: Correctly normalize Windows package paths (thanks @jrick) - #395: Creating the cache key did not handle SCP properly - #386: Fixed help text indentation - #383: Failed `glide get` had been updating files. No longer does this And thanks to @derelk, @franciscocpg, @shawnps, @kngu9, @tugberkugurlu, @rhcarvalho, @gyuho, and @7imon7ays for documentation updates. # Release 0.10.2 (2016-04-06) - Issue #362: Updated docs on how -update-vendored works to help avoid confusion. - Fixed #371: Warn when name/location mismatch. - Fixed #290: On windows Glide was sometimes pulls in current project (thanks tzneal). - Fixed #361: Handle relative imports (thanks tmm1). - Fixed #373: Go 1.7 context package import issues. # Release 0.10.1 (2016-03-25) - Fixed #354: Fixed a situation where a dependency could be fetched when set to ignore. # Release 0.10.0 (2016-03-24) - Issue #293: Added support for importing from Gomfile's (thanks mcuelenaere). - Issue #318: Opt-In to strip VCS metadata from vendor directory. - Issue #297: Adds exclude property for directories in local codebase to exclude from scanning. - Issue #301: Detect version control type from scp style paths (e.g. git@) and from scheme types (e.g., git://). - Issue #339: Add ability to remove nested vendor and Godeps workspaces directories. Note, if Godeps rewriting occured it is undone. The Godeps handling is deprecated from day one and will be removed when most Godeps projects have migrated to vendor folder handling. - Issue #350: More detailed conflict information (commit metadata displayed). - Issue #351: Move to Gitter for chat. - Issue #352: Make Glide installable. The dependencies are checked into the `vendor` folder. # Release 0.9.3 (2016-03-09) - Fixed #324: Glide tries to update ignored package # Release 0.9.2 (2016-03-08) - Fixed issue on #317: Some windows calls had the improper path separator. - Issue #315: Track updated packages to avoid duplicated work (in part by thockin, thanks). - Fixed #312: Don't double-print SetVersion() failure (thanks thockin). - Fixed #311: Don't process deps if 'get' was a non-operation (thanks thockin). - Issue #307: Moving 'already set' to a debug message to cleanup output (thanks thockin). - Fixed #306: Don't call SetVersion twice. There was a place where it was called twice in a logical row (thanks thockin). - Fixed #304: Glide tries to update ignored packages. - Fixed #302: Force update can cause a panic. # Release 0.9.1 (2016-02-24) - Fixed #272: Handling appengine special package case. - Fixed #273: Handle multiple packages in the same directory but handling build tags used in those packages. - Added documentation explaining how import resolution works. - Fixed #275 and #285: Empty directories as package locations reporting errors. Improved the UX and handle the errors. - Fixed #279: Added Go 1.7 support that no longer has GO15VENDOREXPERIMENT. - Issue #267: Added `os` and `arch` import properties to the documentation. - Fixed #267: Glide was only walking the import tree based on build flags for the current OS and Arch. This is a problem for systems like docker that have variation built in. # Release 0.9.0 (2016-02-17) - Fixed #262: Using correct query string merging for go-get queries (thanks gdm85). - Fixed #251: Fixed warning message (thanks james-lawrence). - Adding support for IBM JazzHub. - Fixes #250: When unable to retrieve or set version on a dependency now erroring and exiting with non-0 exit code. - Issue #218: Added `glide rm` command. - Fixed #215: Under some error conditions the package resolver could get into an infinite loop. - Issue #234: Adding more options to the glide.yaml file including license, owners, homepage, etc. See the docs for more detail. - Issue #237: Added Read The Docs support and initial docs. http://glide.readthedocs.org - Issue #248: Uses go env to get value of GO15VENDOREXPERIMENT due to 1.6 enabling by default. - Issue #240: Glide only scans used imports rather than all paths in the tree. The previous behavior is available via a flag. - Fixed #235: Glide on windows writing incorrect slashes to files. - Fixed #227: Fixed ensure when multiple gopaths. - Refactored Glide - Many features broken out into packages. All but `action/` can be used as libraries. - Cookoo is not used anymore - The `action/` package replaces `cmd/` # Release 0.8.3 (2015-12-30) - Issue #198: Instead of stopping `glide install` for a hash failures providing a warning. Failed hash check is currently too aggressive. - Fixed #199: `glide up` on Windows unable to detect dependencies when GOPATH and GOROOT on a different drive or when GOROOT ends in a path separator. - Fixed #194: `glide up` stalling on Windows due to POSIX path separators and path list separators being used. - Fixed #185 and #187: Inaccurate hash being generated for lock file with nested version ranges. - Fixed #182 and #183: Caching on go-import lookups mishandled some prefixes. - Fixed issue in deduping and sub-package names. - Fixed #189: nested dependencies that do not contain VCS information were not being updated properly when --updated-vendored was being used. - Fixed #186: glide up PACKAGE was failing to generate a proper glide.lock file. # Release 0.8.2 (2015-12-21) - Fixed #169: cookoo git url has auth info. Makes glide unbuildable for environments not setup for GitHub. - Fixed #180: the hash in the glide.lock file was not being properly calculated. - Fixed #174: glide get was causing an error when the flag --updated-vendored was being used. - Fixed #175: glide get when the GOPATH isn't setup properly could end up in an infinite loop. # Release 0.8.1 (2015-12-15) - Fixed #163: Was detecting std lib packages when the GOROOT was different at runtime than compile time. - Fixed #165: glide update panics with --no-recursive option. - Added back zip build option to build scripts. This is useful for some environments. # Release 0.8.0 (2015-12-10) - Issues #156 and #85: Added lockfile support (glide.lock). This file records commit id pinned versions of the entire dependency tree. The `glide install` command installs the pinned dependencies from the `glide.lock` file while `glide update` updates the tree and lockfile. Most people should use `glide install` unless they want to intentionally updated the pinned dependencies. `glide install` is able to use concurrency to more quickly install update. - Issues #33 and #159: Glide notifies if a dependency checkout has uncomitted changes. - Issue #146: Glide scans projects not managed by a dependency manager, fetches their dependencies, and pins them in the glide.lock file. - Issue #99: Glide `get` pins dependencies by default and allows a version to be passed in. For example, `glide get github.com/Masterminds/convert#^1.0.0` will fetch `github.com/Masterminds/convert` with a version of `^1.0.0`. - Issue #155: Copying packages from the `GOPATH` is now opt-in. # Release 0.7.2 (2015-11-16) - Fixed #139: glide.yaml file imports being reordered when file written. - Fixed #140: packages in glide.yaml were no longer being deduped. # Release 0.7.1 (2015-11-10) - Fixed #136: Fixed infinite recursion in list and tree commands. - Fixed issue where glide guess listed a null parent. - Fixed #135: Hard failure when home directory not found for cache. - Fixed #137: Some messages not ending in "\n". - Fixed #132 and #133: Build from source directions incorrect (thanks hyPiRion). # Release 0.7.0 (2015-11-02) - Fixed #110: Distribution as .tag.gz instead of .zip. - Issue #126: Added --no-color option to remove color for systems that do not work well with color codes (thanks albrow). - Added caching functionality (some opt-in). - Added global debug flag. - Moved yaml parsing and writing to gopkg.in/yaml.v2 and separated config handling into separate package. - Better godep import handling. - Fixed #98: Godep command name fix (thanks jonboulle). - #52 and #114: Add semantic version (SemVer) support. - #108: Flatten the dependency tree by default. - Fixed #107: Allow `glide get` to retrieve insecure packages with `--insecure` flag. - #105: Import commands accept a filename with the `-f` flag. - Fixed #97: Fixed misspellings (thanks jonboulle). - #96: Allow multiple packages in `glide get`. - #92: Added support to `glide update` to only update a specific package. - #91: `glide list` now displays if a pkg is in vendor, GOPATH, or missing. - Issue #89: More robust GOPATH handling (thanks gcmt). - Fixed #65: Hg commands were not checking out the codebase on the first update. - Fixed #95: Added more detail for errors previously reporting "Oops! exit status 128". - Fixed #86 and #71: Imported package names including a sub-package were checked out to the wrong location. They are not checked out to the right place and multiple instances of the top level repo are merged with error checking. # Release 0.6.1 (2015-09-21) - Fixed #82: C was not recognized as an internal package. - Fixed #84: novendor (nv) command returned directories with no Go code. # Release 0.6.0 (2015-09-16) - #53: Add support for gb-vendor manifest files. - Added `glide tree` command to inspect the code and see the imported packages. - Added `glide list` to see an alphabetized list of imported projects. - Added flatten feature to flatten the vendor tree (thanks interlock). - Fixed #74: Glide guess using the wrong GOROOT locations in some environments (thanks janeczku). - Fixed #76: Glide tree doesn't exclude core libraries with the GOROOT is incorrect (thanks janeczku). - Fixed #81: rebuild command did not look in vendor/ directory - Fixed #77: update failed when a commit id was set for the ref # Release 0.5.1 (2015-08-31) - Fixed #58: Guess command not working. - Fixed #56: Unable to use glide get on golang.org/x/[name]/[subpackage] - Fixed #61: The wrong version of a dependency can be pinned when packages are vendored (no VCS repo associated with them). - Fixed #67: Unable to work go-get redirects. - Fixed #66: 'glide up' now has an --update-vendored (-u) flag to update vendored directories. - Fixed #68: Handling the base where the GOPATH has multiple separated directories. # Release 0.5.0 (2015-08-19) **Glide .5 is a major update breaking some backwards compatability with previous releases.** - Migrated to using the vendor/ directory and the go tools for vendor package management. To leverage this you'll need to set the environment variable GO15VENDOREXPERIMENT=1 and use Go 1.5. - `glide up` is now recursive and walks installed packages if there is no vendor directory. Use the --no-recursive flag to skip this. - Removed GOPATH management. This was needed for vendor package management that's not built into the go toolchain. - Switched to github.com/Masterminds/vcs for VCS integration. - When updating packages are now deleted if the --delete flag is set. This feature is now opt-in. - Fixed #32: Detects VCS type and endpoint changes along with a --force flag to replace the checkout if desired. # Release 0.4.1 (2015-07-13) - Issue #48: When GOPATH not _vendor directory not deleting unused packages. # Release 0.4.0 (2015-07-07) - Issue #34: Delete unused packages on update unless flag set. - Added 'glide create PACKAGE' - Added 'glide exec COMMAND' - Added 'glide get PACKAGE' - Added 'glide pin FILENAME' - Added 'glide guess FILENAME' - Updated help text # Release 0.3.0 (2015-06-17) - Issue #46: If VCS type is set use that rather than go get. - Issue #45: Added git fastpath if configured ref or tag matches current one. (via roblillack) - Issue #30: Added support for changed VCS type to a git repo. (thanks roblillack) - Issue #42: Fixed update for new dependencies where repo not configured. (thanks roblillack) - Issue #25: Added GOOS and GOARCH support. - Issue #35: Updated documentation on what update from existing repos means - Issue #37: Added support to import from GPM and Godep - Issue #36: Added example for shell (bash/zsh) prompt to show the current GOPATH. (thanks eAndrius) - Issue #31: The local Go bin should be higher precedence in the system's PATH (via jarod). - Issue #28: Use HTTPS instead of HTTP for git and hg. (Thanks chendo) - Issue #26: 'glide gopath' is smarter. It now looks for glide.yaml. - Issue #24: Trim whitespace off of package names. (Thanks roblillack) # Release 0.2.0 (2014-10-03) - Issue #15, #18: `glide guess` can guess dependencies for an existing repo. (HUGE thanks to dz0ny) - Issue #14: Glide fails now when YAML is invalid. - Issue #13: cli.go added to Makefile (via roblillack) - Issue #12: InitGlide takes YAML file now - Issue #9: Fixed handling of $SHELL (Thanks roblillack) - Issue #10: Symbolic link uses a relative path now (Thanks roblillack) - Issue #5: Build step is deferred when 'go get' is used to fetch packages. (Thanks gsalgado) - Issue #11: Add GOBIN to glide environment (via dz0ny) - Typos fixed (#17 by lamielle, #16 by roblillack) - Moved the CLI handling to cli.go (github.com/codegangsta/cli) ================================================ FILE: LICENSE ================================================ Glide The Masterminds Copyright (C) 2014-2016, Matt Butcher and Matt Farina Copyright (C) 2016, Hewlett Packard Enterprise Development LP Copyright (C) 2015, Google 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. ================================================ FILE: Makefile ================================================ GLIDE_GO_EXECUTABLE ?= go DIST_DIRS := find * -type d -exec VERSION ?= $(shell git describe --tags) VERSION_INCODE = $(shell perl -ne '/^var version.*"([^"]+)".*$$/ && print "v$$1\n"' glide.go) VERSION_INCHANGELOG = $(shell perl -ne '/^\# Release (\d+(\.\d+)+) / && print "$$1\n"' CHANGELOG.md | head -n1) build: ${GLIDE_GO_EXECUTABLE} build -o glide -ldflags "-X main.version=${VERSION}" glide.go install: build install -d ${DESTDIR}/usr/local/bin/ 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 ./mirrors integration-test: ${GLIDE_GO_EXECUTABLE} build ./glide up ./glide install clean: rm -f ./glide.test rm -f ./glide rm -rf ./dist bootstrap-dist: ${GLIDE_GO_EXECUTABLE} get -u github.com/Masterminds/gox build-all: gox -verbose \ -ldflags "-X main.version=${VERSION}" \ -os="linux darwin windows freebsd openbsd netbsd" \ -arch="amd64 386 armv5 armv6 armv7 arm64 s390x" \ -osarch="!darwin/arm64" \ -output="dist/{{.OS}}-{{.Arch}}/{{.Dir}}" . dist: build-all cd dist && \ $(DIST_DIRS) cp ../LICENSE {} \; && \ $(DIST_DIRS) cp ../README.md {} \; && \ $(DIST_DIRS) tar -zcf glide-${VERSION}-{}.tar.gz {} \; && \ $(DIST_DIRS) zip -r glide-${VERSION}-{}.zip {} \; && \ cd .. verify-version: @if [ "$(VERSION_INCODE)" = "v$(VERSION_INCHANGELOG)" ]; then \ echo "glide: $(VERSION_INCHANGELOG)"; \ elif [ "$(VERSION_INCODE)" = "v$(VERSION_INCHANGELOG)-dev" ]; then \ echo "glide (development): $(VERSION_INCHANGELOG)"; \ else \ echo "Version number in glide.go does not match CHANGELOG.md"; \ echo "glide.go: $(VERSION_INCODE)"; \ echo "CHANGELOG : $(VERSION_INCHANGELOG)"; \ exit 1; \ fi .PHONY: build test install clean bootstrap-dist build-all dist integration-test verify-version ================================================ FILE: README.md ================================================ # Glide: Vendor Package Management for Golang ![glide logo](https://glide.sh/assets/logo-small.png) Are you used to tools such as Cargo, npm, Composer, Nuget, Pip, Maven, Bundler, or other modern package managers? If so, Glide is the comparable Go tool. *Manage your vendor and vendored packages with ease.* Glide is a tool for managing the `vendor` directory within a Go package. This feature, first introduced in Go 1.5, allows each package to have a `vendor` directory containing dependent packages for the project. These vendor packages can be installed by a tool (e.g. glide), similar to `go get` or they can be vendored and distributed with the package. [![Build Status](https://travis-ci.org/Masterminds/glide.svg)](https://travis-ci.org/Masterminds/glide) [![Build status](https://ci.appveyor.com/api/projects/status/3pl4ytgdlfj852li?svg=true&passingText=windows%20build%20passing&failingText=windows%20build%20failing)](https://ci.appveyor.com/project/mattfarina/glide-a8xtg) [![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/glide)](https://goreportcard.com/report/github.com/Masterminds/glide) [![GoDoc](https://godoc.org/github.com/Masterminds/glide?status.svg)](https://godoc.org/github.com/Masterminds/glide) [![Documentation Status](https://readthedocs.org/projects/glide/badge/?version=stable)](http://glide.readthedocs.org/en/stable/?badge=stable) [![Documentation Status](https://readthedocs.org/projects/glide/badge/?version=latest)](http://glide.readthedocs.org/en/latest/?badge=latest) ### Go Modules The Go community is now using Go Modules to handle dependencies. Please consider using that instead of Glide. Glide is now mostly unmaintained. ### Features * Ease dependency management * Support **versioning packages** including [Semantic Versioning 2.0.0](http://semver.org/) support. Any constraint the [`github.com/Masterminds/semver`](https://github.com/Masterminds/semver) package can parse can be used. * Support **aliasing packages** (e.g. for working with github forks) * Remove the need for munging import statements * Work with all of the `go` tools * Support the VCS tools that Go supports: - git - bzr - hg - svn * Support custom local and global plugins (see docs/plugins.md) * 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 system. ## How It Works 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 project is structured like this: ``` - $GOPATH/src/myProject (Your project) | |-- glide.yaml | |-- glide.lock | |-- main.go (Your main go code can live here) | |-- mySubpackage (You can create your own subpackages, too) | | | |-- foo.go | |-- vendor |-- github.com | |-- Masterminds | |-- ... etc. ``` *Take a look at [the Glide source code](http://github.com/Masterminds/glide) to see this philosophy in action.* ## Install The easiest way to install the latest release on Mac or Linux is with the following script: ``` curl https://glide.sh/get | sh ``` On Mac OS X you can also install the latest release via [Homebrew](https://github.com/Homebrew/homebrew): ``` $ brew install glide ``` On Ubuntu Precise (12.04), Trusty (14.04), Wily (15.10) or Xenial (16.04) you can install from our PPA: ``` sudo add-apt-repository ppa:masterminds/glide && sudo apt-get update sudo apt-get install glide ``` On Ubuntu Zesty (17.04) the package is called `golang-glide`. [Binary packages](https://github.com/Masterminds/glide/releases) are available for Mac, Linux and Windows. For a development version it is also possible to `go get github.com/Masterminds/glide`. To build from source you can: 1. Clone this repository into `$GOPATH/src/github.com/Masterminds/glide` and change directory into it 2. If you are using Go 1.5 ensure the environment variable GO15VENDOREXPERIMENT is set, for example by running `export GO15VENDOREXPERIMENT=1`. In Go 1.6 it is enabled by default and in Go 1.7 it is always enabled without the ability to turn it off. 3. Run `make build` This will leave you with `./glide`, which you can put in your `$PATH` if you'd like. (You can also take a look at `make install` to install for you.) The Glide repo has now been configured to use glide to manage itself, too. ## Usage ``` $ glide create # Start a new workspace $ open glide.yaml # and edit away! $ glide get github.com/Masterminds/cookoo # Get a package and add to glide.yaml $ glide install # Install packages and dependencies # work, work, work $ go build # Go tools work normally $ glide up # Update to newest versions of the package ``` Check out the `glide.yaml` in this directory, or examples in the `docs/` directory. ### glide create (aliased to init) 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 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] You can download one or more packages to your `vendor` directory and have it added to your `glide.yaml` file with `glide get`. ``` $ glide get github.com/Masterminds/cookoo ``` When `glide get` is used it will introspect the listed package to resolve its dependencies including using Godep, GPM, Gom, and GB config files. ### 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 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. 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`). 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 `glide install`. ``` $ glide install ``` This will read the `glide.lock` file and install the commit id specific versions there. When the `glide.lock` file doesn't tie to the `glide.yaml` file, such as there being a change, it will provide a warning. Running `glide up` will recreate the `glide.lock` file when updating the dependency tree. 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 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`. $ go test $(glide novendor) This will run `go test` over all directories of your project except the `vendor` directory. ## glide name When you're scripting with Glide there are occasions where you need to know the name of the package you're working on. `glide name` returns the name of the package listed in the `glide.yaml` file. ### glide tree Glide includes a few commands that inspect code and give you details about what is imported. `glide tree` is one such command. Running it gives data like this: ``` $ glide tree github.com/Masterminds/glide github.com/Masterminds/cookoo (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo) github.com/Masterminds/cookoo/io (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/io) github.com/Masterminds/glide/cmd (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/cmd) github.com/Masterminds/cookoo (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo) github.com/Masterminds/cookoo/io (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/io) github.com/Masterminds/glide/gb (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/gb) github.com/Masterminds/glide/util (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/util) github.com/Masterminds/vcs (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/vcs) github.com/Masterminds/glide/yaml (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/yaml) github.com/Masterminds/glide/util (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/util) github.com/Masterminds/vcs (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/vcs) github.com/Masterminds/vcs (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/vcs) gopkg.in/yaml.v2 (/Users/mfarina/Code/go/src/gopkg.in/yaml.v2) github.com/Masterminds/semver (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/semver) github.com/Masterminds/vcs (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/vcs) github.com/codegangsta/cli (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/codegangsta/cli) github.com/codegangsta/cli (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/codegangsta/cli) github.com/Masterminds/cookoo (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo) github.com/Masterminds/cookoo/io (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/cookoo/io) github.com/Masterminds/glide/gb (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/gb) github.com/Masterminds/glide/util (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/util) github.com/Masterminds/vcs (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/vcs) github.com/Masterminds/glide/yaml (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/yaml) github.com/Masterminds/glide/util (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/util) github.com/Masterminds/vcs (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/vcs) github.com/Masterminds/vcs (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/vcs) gopkg.in/yaml.v2 (/Users/mfarina/Code/go/src/gopkg.in/yaml.v2) github.com/Masterminds/semver (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/semver) github.com/Masterminds/vcs (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/Masterminds/vcs) github.com/codegangsta/cli (/Users/mfarina/Code/go/src/github.com/Masterminds/glide/vendor/github.com/codegangsta/cli) ``` This shows a tree of imports, excluding core libraries. Because vendoring makes it possible for the same package to live in multiple 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 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 help Print the glide help. ``` $ glide help ``` ### glide --version Print the version and exit. ``` $ glide --version 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 2. It declares external dependencies A brief `glide.yaml` file looks like this: ```yaml package: github.com/Masterminds/glide import: - package: github.com/Masterminds/semver - package: github.com/Masterminds/cookoo version: ^1.2.0 repo: git@github.com:Masterminds/cookoo.git ``` The above tells `glide` that... 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. When Glide reads the definition for the second library, it will get the repo from the source in `repo`, checkout the latest version between 1.2.0 and 2.0.0, and put it in `github.com/Masterminds/cookoo` in the `vendor` directory. (Note that `package` and `repo` can be completely different) **TIP:** The version is either VCS dependent and can be anything that can be checked out or a semantic version constraint that can be parsed by the [`github.com/ Masterminds/semver`](https://github.com/Masterminds/semver) package. For example, with Git this can be a branch, tag, or hash. This varies and depends on what's supported in the VCS. **TIP:** In general, you are advised to use the *base package name* for importing a package, not a subpackage name. For example, use `github.com/kylelemons/go-gypsy` and not `github.com/kylelemons/go-gypsy/yaml`. ## Supported Version Control Systems The Git, SVN, Mercurial (Hg), and Bzr source control systems are supported. This happens through the [vcs package](https://github.com/masterminds/vcs). ## Frequently Asked Questions (F.A.Q.) #### Q: Why does Glide have the concept of sub-packages when Go doesn't? In Go every directory is a package. This works well when you have one repo containing all of your packages. When you have different packages in different VCS locations things become a bit more complicated. A project containing a collection of packages should be handled with the same information including the version. By grouping packages this way we are able to manage the related information. #### Q: bzr (or hg) is not working the way I expected. Why? These are works in progress, and may need some additional tuning. Please take a look at the [vcs package](https://github.com/masterminds/vcs). If you see a better way to handle it please let us know. #### Q: Should I check `vendor/` into version control? That's up to you. It's not necessary, but it may also cause you extra work and lots of extra space in your VCS. There may also be unforeseen errors ([see an example](https://github.com/mattfarina/golang-broken-vendor)). #### Q: How do I import settings from GPM, Godep, gom or gb? There are two parts to importing. 1. If a package you import has configuration for GPM, Godep, gom or gb Glide will recursively install the dependencies automatically. 2. If you would like to import configuration from GPM, Godep, gom or gb to Glide see the `glide import` command. For example, you can run `glide import godep` for Glide to detect the projects Godep configuration and generate a `glide.yaml` file for you. Each of these will merge your existing `glide.yaml` file with the dependencies it finds for those managers, and then emit the file as output. **It will not overwrite your glide.yaml file.** You can write it to file like this: ``` $ glide import godep -f glide.yaml ``` #### Q: Can Glide fetch a package based on OS or Arch? A: Yes. Using the `os` and `arch` fields on a `package`, you can specify which OSes and architectures the package should be fetched for. For example, the following package will only be fetched for 64-bit Darwin/OSX systems: ```yaml - package: some/package os: - darwin arch: - amd64 ``` The package will not be fetched for other architectures or OSes. ## LICENSE This package is made available under an MIT-style license. See LICENSE.txt. ## Thanks! We owe a huge debt of gratitude to the [GPM and GVP](https://github.com/pote/gpm) projects, which inspired many of the features of this package. If `glide` isn't the right Go project manager for you, check out those. The Composer (PHP), npm (JavaScript), and Bundler (Ruby) projects all inspired various aspects of this tool, as well. ## The Name Aside from being catchy, "glide" is a contraction of "Go Elide". The idea is to compress the tasks that normally take us lots of time into a just a few seconds. ================================================ FILE: action/about.go ================================================ package action import "github.com/Masterminds/glide/msg" const aboutMessage = ` Glide: Vendor Package Management for Go. Manage your vendor and vendored packages with ease. Name: Aside from being catchy, "glide" is a contraction of "Go Elide". The idea is to compress the tasks that normally take us lots of time into a just a few seconds. To file issues, obtain the source, or learn more visit: https://github.com/Masterminds/glide Glide is licensed under the MIT License: Copyright (C) 2014-2015, Matt Butcher and Matt Farina Copyright (C) 2015, Google 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.` // About prints information about Glide. func About() { msg.Puts(aboutMessage) } ================================================ FILE: action/about_test.go ================================================ package action import ( "bytes" "testing" "github.com/Masterminds/glide/msg" ) func TestAbout(t *testing.T) { var buf bytes.Buffer old := msg.Default.Stdout msg.Default.Stdout = &buf About() if buf.Len() < len(aboutMessage) { t.Errorf("expected this to match aboutMessage: %q", buf.String()) } msg.Default.Stdout = old } ================================================ FILE: action/cache.go ================================================ package action import ( "os" "github.com/Masterminds/glide/cache" "github.com/Masterminds/glide/msg" ) // CacheClear clears the Glide cache func CacheClear() { l := cache.Location() err := os.RemoveAll(l) if err != nil { msg.Die("Unable to clear the cache: %s", err) } cache.SetupReset() cache.Setup() msg.Info("Glide cache has been cleared.") } ================================================ FILE: action/config_wizard.go ================================================ package action import ( "os" "os/exec" "path/filepath" "regexp" "strings" "github.com/Masterminds/glide/cache" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/semver" "github.com/Masterminds/vcs" ) // 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 { msg.Info("Unable to find a glide.yaml file. Would you like to create one now? Yes (Y) or No (N)") bres := msg.PromptUntilYorN() if bres { // Guess deps conf := guessDeps(base, false) // Write YAML if err := conf.WriteFile(glidefile); err != nil { msg.Die("Could not save %s: %s", glidefile, err) } } else { msg.Err("Unable to find configuration file. Please create configuration information to continue.") } } conf := EnsureConfig() cache.Setup() msg.Info("Looking for dependencies to make suggestions on") msg.Info("--> Scanning for dependencies not using version ranges") msg.Info("--> Scanning for dependencies using commit ids") var deps []*cfg.Dependency for _, dep := range conf.Imports { if wizardLookInto(dep) { deps = append(deps, dep) } } for _, dep := range conf.DevImports { if wizardLookInto(dep) { deps = append(deps, dep) } } msg.Info("Gathering information on each dependency") msg.Info("--> This may take a moment. Especially on a codebase with many dependencies") msg.Info("--> Gathering release information for dependencies") msg.Info("--> Looking for dependency imports where versions are commit ids") for _, dep := range deps { wizardFindVersions(dep) } var changes int for _, dep := range deps { remote := dep.Remote() // First check, ask if the tag should be used instead of the commit id for it. cur := cache.MemCurrent(remote) if cur != "" && cur != dep.Reference { wizardSugOnce() var dres bool asked, use, val := wizardOnce("current") if !use { dres = wizardAskCurrent(cur, dep) } if !asked { as := wizardRemember() wizardSetOnce("current", as, dres) } if asked && use { dres = val.(bool) } if dres { msg.Info("Updating %s to use the tag %s instead of commit id %s", dep.Name, cur, dep.Reference) dep.Reference = cur changes++ } } // Second check, if no version is being used and there's a semver release ask about latest. memlatest := cache.MemLatest(remote) if dep.Reference == "" && memlatest != "" { wizardSugOnce() var dres bool asked, use, val := wizardOnce("latest") if !use { dres = wizardAskLatest(memlatest, dep) } if !asked { as := wizardRemember() wizardSetOnce("latest", as, dres) } if asked && use { dres = val.(bool) } if dres { msg.Info("Updating %s to use the release %s instead of no release", dep.Name, memlatest) dep.Reference = memlatest changes++ } } // Third check, if the version is semver offer to use a range instead. sv, err := semver.NewVersion(dep.Reference) if err == nil { wizardSugOnce() var res string asked, use, val := wizardOnce("range") if !use { res = wizardAskRange(sv, dep) } if !asked { as := wizardRemember() wizardSetOnce("range", as, res) } if asked && use { res = val.(string) } if res == "m" { r := "^" + sv.String() msg.Info("Updating %s to use the range %s instead of commit id %s", dep.Name, r, dep.Reference) dep.Reference = r changes++ } else if res == "p" { r := "~" + sv.String() msg.Info("Updating %s to use the range %s instead of commit id %s", dep.Name, r, dep.Reference) dep.Reference = r changes++ } } } if changes > 0 { msg.Info("Configuration changes have been made. Would you like to write these") msg.Info("changes to your configuration file? Yes (Y) or No (N)") dres := msg.PromptUntilYorN() if dres { msg.Info("Writing updates to configuration file (%s)", glidefile) if err := conf.WriteFile(glidefile); err != nil { msg.Die("Could not save %s: %s", glidefile, err) } msg.Info("You can now edit the glide.yaml file.:") msg.Info("--> For more information on versions and ranges see https://glide.sh/docs/versions/") msg.Info("--> For details on additional metadata see https://glide.sh/docs/glide.yaml/") } else { msg.Warn("Change not written to configuration file") } } else { msg.Info("No proposed changes found. Have a nice day.") } } var wizardOnceVal = make(map[string]interface{}) var wizardOnceDo = make(map[string]bool) var wizardOnceAsked = make(map[string]bool) var wizardSuggeseOnce bool func wizardSugOnce() { if !wizardSuggeseOnce { msg.Info("Here are some suggestions...") } wizardSuggeseOnce = true } // Returns if it's you should prompt, if not prompt if you should use stored value, // and stored value if it has one. func wizardOnce(name string) (bool, bool, interface{}) { return wizardOnceAsked[name], wizardOnceDo[name], wizardOnceVal[name] } func wizardSetOnce(name string, prompt bool, val interface{}) { wizardOnceAsked[name] = true wizardOnceDo[name] = prompt wizardOnceVal[name] = val } func wizardRemember() bool { msg.Info("Would you like to remember the previous decision and apply it to future") msg.Info("dependencies? Yes (Y) or No (N)") return msg.PromptUntilYorN() } func wizardAskRange(ver *semver.Version, d *cfg.Dependency) string { vstr := ver.String() msg.Info("The package %s appears to use semantic versions (http://semver.org).", d.Name) msg.Info("Would you like to track the latest minor or patch releases (major.minor.patch)?") msg.Info("The choices are:") msg.Info(" - Tracking minor version releases would use '>= %s, < %d.0.0' ('^%s')", vstr, ver.Major()+1, vstr) msg.Info(" - Tracking patch version releases would use '>= %s, < %d.%d.0' ('~%s')", vstr, ver.Major(), ver.Minor()+1, vstr) msg.Info(" - Skip using ranges\n") msg.Info("For more information on Glide versions and ranges see https://glide.sh/docs/versions") msg.Info("Minor (M), Patch (P), or Skip Ranges (S)?") res, err := msg.PromptUntil([]string{"minor", "m", "patch", "p", "skip ranges", "s"}) if err != nil { msg.Die("Error processing response: %s", err) } if res == "m" || res == "minor" { return "m" } else if res == "p" || res == "patch" { return "p" } return "s" } func wizardAskCurrent(cur string, d *cfg.Dependency) bool { msg.Info("The package %s is currently set to use the version %s.", d.Name, d.Reference) msg.Info("There is an equivalent semantic version (http://semver.org) release of %s. Would", cur) msg.Info("you like to use that instead? Yes (Y) or No (N)") return msg.PromptUntilYorN() } func wizardAskLatest(latest string, d *cfg.Dependency) bool { msg.Info("The package %s appears to have Semantic Version releases (http://semver.org). ", d.Name) msg.Info("The latest release is %s. You are currently not using a release. Would you like", latest) msg.Info("to use this release? Yes (Y) or No (N)") return msg.PromptUntilYorN() } func wizardLookInto(d *cfg.Dependency) bool { _, err := semver.NewConstraint(d.Reference) // The existing version is already a valid semver constraint so we skip suggestions. if err == nil { return false } return true } // Note, this really needs a simpler name. var createGitParseVersion = regexp.MustCompile(`(?m-s)(?:tags)/(\S+)$`) func wizardFindVersions(d *cfg.Dependency) { l := cache.Location() remote := d.Remote() key, err := cache.Key(remote) if err != nil { msg.Debug("Problem generating cache key for %s: %s", remote, err) return } local := filepath.Join(l, "src", key) repo, err := vcs.NewRepo(remote, local) if err != nil { msg.Debug("Problem getting repo instance: %s", err) return } var useLocal bool if _, err = os.Stat(local); err == nil { useLocal = true } // Git endpoints allow for querying without fetching the codebase locally. // We try that first to avoid fetching right away. Is this premature // optimization? cc := true if !useLocal && repo.Vcs() == vcs.Git { out, err2 := exec.Command("git", "ls-remote", remote).CombinedOutput() if err2 == nil { cache.MemTouch(remote) cc = false lines := strings.Split(string(out), "\n") for _, i := range lines { ti := strings.TrimSpace(i) if found := createGitParseVersion.FindString(ti); found != "" { tg := strings.TrimPrefix(strings.TrimSuffix(found, "^{}"), "tags/") cache.MemPut(remote, tg) if d.Reference != "" && strings.HasPrefix(ti, d.Reference) { cache.MemSetCurrent(remote, tg) } } } } } if cc { cache.Lock(key) cache.MemTouch(remote) if _, err = os.Stat(local); os.IsNotExist(err) { repo.Get() branch := findCurrentBranch(repo) c := cache.RepoInfo{DefaultBranch: branch} err = cache.SaveRepoData(key, c) if err != nil { msg.Debug("Error saving cache repo details: %s", err) } } else { repo.Update() } tgs, err := repo.Tags() if err != nil { msg.Debug("Problem getting tags: %s", err) } else { for _, v := range tgs { cache.MemPut(remote, v) } } if d.Reference != "" && repo.IsReference(d.Reference) { tgs, err = repo.TagsFromCommit(d.Reference) if err != nil { msg.Debug("Problem getting tags for commit: %s", err) } else { if len(tgs) > 0 { for _, v := range tgs { if !(repo.Vcs() == vcs.Hg && v == "tip") { cache.MemSetCurrent(remote, v) } } } } } cache.Unlock(key) } } func findCurrentBranch(repo vcs.Repo) string { msg.Debug("Attempting to find current branch for %s", repo.Remote()) // Svn and Bzr don't have default branches. if repo.Vcs() == vcs.Svn || repo.Vcs() == vcs.Bzr { return "" } if repo.Vcs() == vcs.Git || repo.Vcs() == vcs.Hg { ver, err := repo.Current() if err != nil { msg.Debug("Unable to find current branch for %s, error: %s", repo.Remote(), err) return "" } return ver } return "" } ================================================ FILE: action/create.go ================================================ package action import ( "os" "path/filepath" "sort" "strings" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/dependency" "github.com/Masterminds/glide/gb" "github.com/Masterminds/glide/godep" "github.com/Masterminds/glide/gpm" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/glide/util" ) // Create creates/initializes a new Glide repository. // // This will fail if a glide.yaml already exists. // // By default, this will scan the present source code directory for dependencies. // // If skipImport is set to true, this will not attempt to import from an existing // GPM, Godep, or GB project if one should exist. However, it will still attempt // to read the local source to determine required packages. func Create(base string, skipImport, nonInteractive bool) { glidefile := gpath.GlideFile // Guard against overwrites. guardYAML(glidefile) // Guess deps conf := guessDeps(base, skipImport) // Write YAML msg.Info("Writing configuration file (%s)", glidefile) if err := conf.WriteFile(glidefile); err != nil { msg.Die("Could not save %s: %s", glidefile, err) } var res bool if !nonInteractive { msg.Info("Would you like Glide to help you find ways to improve your glide.yaml configuration?") msg.Info("If you want to revisit this step you can use the config-wizard command at any time.") msg.Info("Yes (Y) or No (N)?") res = msg.PromptUntilYorN() if res { ConfigWizard(base) } } if !res { msg.Info("You can now edit the glide.yaml file. Consider:") msg.Info("--> Using versions and ranges. See https://glide.sh/docs/versions/") msg.Info("--> Adding additional metadata. See https://glide.sh/docs/glide.yaml/") msg.Info("--> Running the config-wizard command to improve the versions in your configuration") } } // guardYAML fails if the given file already exists. // // This prevents an important file from being overwritten. func guardYAML(filename string) { if _, err := os.Stat(filename); err == nil { msg.Die("Cowardly refusing to overwrite existing YAML.") } } // guessDeps attempts to resolve all of the dependencies for a given project. // // base is the directory to start with. // skipImport will skip running the automatic imports. // // FIXME: This function is likely a one-off that has a more standard alternative. // It's also long and could use a refactor. func guessDeps(base string, skipImport bool) *cfg.Config { buildContext, err := util.GetBuildContext() if err != nil { msg.Die("Failed to build an import context: %s", err) } name := buildContext.PackageName(base) msg.Info("Generating a YAML configuration file and guessing the dependencies") config := new(cfg.Config) // Get the name of the top level package config.Name = name // Import by looking at other package managers and looking over the // entire directory structure. // Attempt to import from other package managers. if !skipImport { guessImportDeps(base, config) } importLen := len(config.Imports) if importLen == 0 { msg.Info("Scanning code to look for dependencies") } else { msg.Info("Scanning code to look for dependencies not found in import") } // Resolve dependencies by looking at the tree. r, err := dependency.NewResolver(base) if err != nil { msg.Die("Error creating a dependency resolver: %s", err) } // When creating resolve the test dependencies as well as the application ones. r.ResolveTest = true h := &dependency.DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}} r.Handler = h sortable, testSortable, err := r.ResolveLocal(false) if err != nil { msg.Die("Error resolving local dependencies: %s", err) } sort.Strings(sortable) sort.Strings(testSortable) vpath := r.VendorDir if !strings.HasSuffix(vpath, "/") { vpath = vpath + string(os.PathSeparator) } for _, pa := range sortable { n := strings.TrimPrefix(pa, vpath) root, subpkg := util.NormalizeName(n) if !config.Imports.Has(root) && root != config.Name { msg.Info("--> Found reference to %s\n", n) d := &cfg.Dependency{ Name: root, } if len(subpkg) > 0 { d.Subpackages = []string{subpkg} } config.Imports = append(config.Imports, d) } else if config.Imports.Has(root) { if len(subpkg) > 0 { subpkg = strings.TrimPrefix(subpkg, "/") d := config.Imports.Get(root) if !d.HasSubpackage(subpkg) { msg.Info("--> Adding sub-package %s to %s\n", subpkg, root) d.Subpackages = append(d.Subpackages, subpkg) } } } } for _, pa := range testSortable { n := strings.TrimPrefix(pa, vpath) root, subpkg := util.NormalizeName(n) if config.Imports.Has(root) && root != config.Name { msg.Debug("--> Found test reference to %s already listed as an import", n) } else if !config.DevImports.Has(root) && root != config.Name { msg.Info("--> Found test reference to %s", n) d := &cfg.Dependency{ Name: root, } if len(subpkg) > 0 { d.Subpackages = []string{subpkg} } config.DevImports = append(config.DevImports, d) } else if config.DevImports.Has(root) { if len(subpkg) > 0 { subpkg = strings.TrimPrefix(subpkg, "/") d := config.DevImports.Get(root) if !d.HasSubpackage(subpkg) { msg.Info("--> Adding test sub-package %s to %s\n", subpkg, root) d.Subpackages = append(d.Subpackages, subpkg) } } } } if len(config.Imports) == importLen && importLen != 0 { msg.Info("--> Code scanning found no additional imports") } return config } func guessImportDeps(base string, config *cfg.Config) { msg.Info("Attempting to import from other package managers (use --skip-import to skip)") deps := []*cfg.Dependency{} absBase, err := filepath.Abs(base) if err != nil { msg.Die("Failed to resolve location of %s: %s", base, err) } if d, ok := guessImportGodep(absBase); ok { msg.Info("Importing Godep configuration") msg.Warn("Godep uses commit id versions. Consider using Semantic Versions with Glide") deps = d } else if d, ok := guessImportGPM(absBase); ok { msg.Info("Importing GPM configuration") deps = d } else if d, ok := guessImportGB(absBase); ok { msg.Info("Importing GB configuration") deps = d } for _, i := range deps { if i.Reference == "" { msg.Info("--> Found imported reference to %s", i.Name) } else { msg.Info("--> Found imported reference to %s at revision %s", i.Name, i.Reference) } config.Imports = append(config.Imports, i) } } func guessImportGodep(dir string) ([]*cfg.Dependency, bool) { d, err := godep.Parse(dir) if err != nil || len(d) == 0 { return []*cfg.Dependency{}, false } return d, true } func guessImportGPM(dir string) ([]*cfg.Dependency, bool) { d, err := gpm.Parse(dir) if err != nil || len(d) == 0 { return []*cfg.Dependency{}, false } return d, true } func guessImportGB(dir string) ([]*cfg.Dependency, bool) { d, err := gb.Parse(dir) if err != nil || len(d) == 0 { return []*cfg.Dependency{}, false } return d, true } ================================================ FILE: action/debug.go ================================================ package action import ( "github.com/Masterminds/glide/msg" ) // Debug sets the debugging flags across components. func Debug(on bool) { msg.Default.IsDebugging = on } // Quiet sets the quiet flags across components. func Quiet(on bool) { msg.Default.Quiet = on } // NoColor sets the color flags. func NoColor(on bool) { msg.Default.NoColor = on } ================================================ FILE: action/doc.go ================================================ // Package action provides implementations for every Glide command. // // This is not a general-purpose library. It is the main flow controller for Glide. // // The main glide package acts as a Facade, with this package providing the // implementation. This package should know nothing of the command line flags or // runtime characteristics. However, this package is allowed to control the flow // of the application, including termination. So actions may call `msg.Die()` to // immediately stop execution of the program. // // In general, actions are not required to function as library functions, nor as // concurrency-safe functions. package action ================================================ FILE: action/ensure.go ================================================ package action import ( "io/ioutil" "os" "os/exec" "path" "path/filepath" "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" ) // EnsureConfig loads and returns a config file. // // Any error will cause an immediate exit, with an error printed to Stderr. func EnsureConfig() *cfg.Config { yamlpath, err := gpath.Glide() if err != nil { msg.ExitCode(2) msg.Die("Failed to find %s file in directory tree: %s", gpath.GlideFile, err) } yml, err := ioutil.ReadFile(yamlpath) if err != nil { msg.ExitCode(2) msg.Die("Failed to load %s: %s", yamlpath, err) } conf, err := cfg.ConfigFromYaml(yml) if err != nil { msg.ExitCode(3) msg.Die("Failed to parse %s: %s", yamlpath, err) } b := filepath.Dir(yamlpath) buildContext, err := util.GetBuildContext() if err != nil { msg.Die("Failed to build an import context while ensuring config: %s", err) } cwd, err := os.Getwd() if err != nil { msg.Err("Unable to get the current working directory") } else { // Determining a package name requires a relative path b, err = filepath.Rel(b, cwd) if err == nil { name := buildContext.PackageName(b) if name != conf.Name { msg.Warn("The name listed in the config file (%s) does not match the current location (%s)", conf.Name, name) } } else { msg.Warn("Problem finding the config file path (%s) relative to the current directory (%s): %s", b, cwd, err) } } err = mirrors.Load() if err != nil { msg.Err("Unable to load mirrors: %s", err) } return conf } // EnsureGoVendor ensures that the Go version is correct. func EnsureGoVendor() { // 6l was removed in 1.5, when vendoring was introduced. cmd := exec.Command(goExecutable(), "tool", "6l") if _, err := cmd.CombinedOutput(); err == nil { msg.Warn("You must install the Go 1.5 or greater toolchain to work with Glide.\n") os.Exit(1) } // Check if this is go15, which requires GO15VENDOREXPERIMENT // Any release after go15 does not require that env var. cmd = exec.Command(goExecutable(), "version") if out, err := cmd.CombinedOutput(); err != nil { msg.Err("Error getting version: %s.\n", err) os.Exit(1) } else if strings.HasPrefix(string(out), "go version 1.5") { // This works with 1.5 and 1.6. cmd = exec.Command(goExecutable(), "env", "GO15VENDOREXPERIMENT") if out, err := cmd.CombinedOutput(); err != nil { msg.Err("Error looking for $GOVENDOREXPERIMENT: %s.\n", err) os.Exit(1) } else if strings.TrimSpace(string(out)) != "1" { msg.Err("To use Glide, you must set GO15VENDOREXPERIMENT=1") os.Exit(1) } } // In the case where vendoring is explicitly disabled, balk. if os.Getenv("GO15VENDOREXPERIMENT") == "0" { msg.Err("To use Glide, you must set GO15VENDOREXPERIMENT=1") os.Exit(1) } // Verify the setup isn't for the old version of glide. That is, this is // no longer assuming the _vendor directory as the GOPATH. Inform of // the change. if _, err := os.Stat("_vendor/"); err == nil { msg.Warn(`Your setup appears to be for the previous version of Glide. Previously, vendor packages were stored in _vendor/src/ and _vendor was set as your GOPATH. As of Go 1.5 the go tools recognize the vendor directory as a location for these files. Glide has embraced this. Please remove the _vendor directory or move the _vendor/src/ directory to vendor/.` + "\n") os.Exit(1) } } // EnsureVendorDir ensures that a vendor/ directory is present in the cwd. func EnsureVendorDir() { fi, err := os.Stat(gpath.VendorDir) if err != nil { msg.Debug("Creating %s", gpath.VendorDir) if err := os.MkdirAll(gpath.VendorDir, os.ModeDir|0755); err != nil { msg.Die("Could not create %s: %s", gpath.VendorDir, err) } } else if !fi.IsDir() { msg.Die("Vendor is not a directory") } } // EnsureGopath fails if GOPATH is not set, or if $GOPATH/src is missing. // // Otherwise it returns the value of GOPATH. func EnsureGopath() string { gps := gpath.Gopaths() if len(gps) == 0 { msg.Die("$GOPATH is not set.") } for _, gp := range gps { _, err := os.Stat(path.Join(gp, "src")) if err != nil { msg.Warn("%s", err) continue } return gp } msg.Err("Could not find any of %s/src.\n", strings.Join(gps, "/src, ")) msg.Info("As of Glide 0.5/Go 1.5, this is required.\n") msg.Die("Without src, cannot continue.") return "" } // goExecutable checks for a set environment variable of GLIDE_GO_EXECUTABLE // for the go executable name. The Google App Engine SDK ships with a python // wrapper called goapp // // Example usage: GLIDE_GO_EXECUTABLE=goapp glide install func goExecutable() string { goExecutable := os.Getenv("GLIDE_GO_EXECUTABLE") if len(goExecutable) <= 0 { goExecutable = "go" } return goExecutable } ================================================ FILE: action/get.go ================================================ package action import ( "fmt" "path/filepath" "strings" "github.com/Masterminds/glide/cache" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/godep" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/glide/repo" "github.com/Masterminds/glide/util" "github.com/Masterminds/semver" ) // 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, stripVendor, nonInteract, testDeps bool) { cache.SystemLock() base := gpath.Basepath() EnsureGopath() EnsureVendorDir() conf := EnsureConfig() glidefile, err := gpath.Glide() if err != nil { msg.Die("Could not find Glide file: %s", err) } // Add the packages to the config. if count, err2 := addPkgsToConfig(conf, names, insecure, nonInteract, testDeps); err2 != nil { msg.Die("Failed to get new packages: %s", err2) } else if count == 0 { msg.Warn("Nothing to do") return } // Fetch the new packages. Can't resolve versions via installer.Update if // get is called while the vendor/ directory is empty so we checkout // everything. err = installer.Checkout(conf) if err != nil { msg.Die("Failed to checkout packages: %s", err) } // Prior to resolving dependencies we need to start working with a clone // of the conf because we'll be making real changes to it. confcopy := conf.Clone() if !skipRecursive { // Get all repos and update them. // TODO: Can we streamline this in any way? The reason that we update all // of the dependencies is that we need to re-negotiate versions. For example, // if an existing dependency has the constraint >1.0 and this new package // adds the constraint <2.0, then this may re-resolve the existing dependency // to be between 1.0 and 2.0. But changing that dependency may then result // in that dependency's dependencies changing... so we sorta do the whole // thing to be safe. err = installer.Update(confcopy) if err != nil { msg.Die("Could not update packages: %s", err) } } // Set Reference if err := repo.SetReference(confcopy, installer.ResolveTest); err != nil { msg.Err("Failed to set references: %s", err) } err = installer.Export(confcopy) if err != nil { msg.Die("Unable to export dependencies to vendor directory: %s", err) } // Write YAML if err := conf.WriteFile(glidefile); err != nil { msg.Die("Failed to write glide YAML file: %s", err) } if !skipRecursive { // Write lock if stripVendor { confcopy = godep.RemoveGodepSubpackages(confcopy) } writeLock(conf, confcopy, base) } else { msg.Warn("Skipping lockfile generation because full dependency tree is not being calculated") } if stripVendor { msg.Info("Removing nested vendor and Godeps/_workspace directories...") err := gpath.StripVendor() if err != nil { msg.Err("Unable to strip vendor directories: %s", err) } } } func writeLock(conf, confcopy *cfg.Config, base string) { hash, err := conf.Hash() if err != nil { msg.Die("Failed to generate config hash. Unable to generate lock file.") } 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) } } // addPkgsToConfig adds the given packages to the config file. // // Along the way it: // - ensures that this package is not in the ignore list // - checks to see if this is already in the dependency list. // - splits version of of package name and adds the version attribute // - separates repo from packages // - sets up insecure repo URLs where necessary // - generates a list of subpackages func addPkgsToConfig(conf *cfg.Config, names []string, insecure, nonInteract, testDeps bool) (int, error) { if len(names) == 1 { msg.Info("Preparing to install %d package.", len(names)) } else { msg.Info("Preparing to install %d packages.", len(names)) } numAdded := 0 for _, name := range names { var version string parts := strings.Split(name, "#") if len(parts) > 1 { name = parts[0] version = parts[1] } msg.Info("Attempting to get package %s", name) root, subpkg := util.NormalizeName(name) if len(root) == 0 { return 0, fmt.Errorf("Package name is required for %q.", name) } if conf.HasDependency(root) { var moved bool var dep *cfg.Dependency // Move from DevImports to Imports if !testDeps && !conf.Imports.Has(root) && conf.DevImports.Has(root) { dep = conf.DevImports.Get(root) conf.Imports = append(conf.Imports, dep) conf.DevImports = conf.DevImports.Remove(root) moved = true numAdded++ msg.Info("--> Moving %s from testImport to import", root) } else if testDeps && conf.Imports.Has(root) { msg.Warn("--> Test dependency %s already listed as import", root) } // Check if the subpackage is present. if subpkg != "" { if dep == nil { dep = conf.Imports.Get(root) if dep == nil && testDeps { dep = conf.DevImports.Get(root) } } if dep.HasSubpackage(subpkg) { if !moved { msg.Warn("--> Package %q is already in glide.yaml. Skipping", name) } } else { dep.Subpackages = append(dep.Subpackages, subpkg) msg.Info("--> Adding sub-package %s to existing import %s", subpkg, root) numAdded++ } } else if !moved { msg.Warn("--> Package %q is already in glide.yaml. Skipping", root) } continue } if conf.HasIgnore(root) { msg.Warn("--> Package %q is set to be ignored in glide.yaml. Skipping", root) continue } dep := &cfg.Dependency{ Name: root, } // When retriving from an insecure location set the repo to the // insecure location. if insecure { dep.Repository = "http://" + root } if version != "" { dep.Reference = version } else if !nonInteract { getWizard(dep) } if len(subpkg) > 0 { dep.Subpackages = []string{subpkg} } if dep.Reference != "" { msg.Info("--> Adding %s to your configuration with the version %s", dep.Name, dep.Reference) } else { msg.Info("--> Adding %s to your configuration", dep.Name) } if testDeps { conf.DevImports = append(conf.DevImports, dep) } else { conf.Imports = append(conf.Imports, dep) } numAdded++ } return numAdded, nil } func getWizard(dep *cfg.Dependency) { remote := dep.Remote() // Lookup dependency info and store in cache. msg.Info("--> Gathering release information for %s", dep.Name) wizardFindVersions(dep) memlatest := cache.MemLatest(remote) if memlatest != "" { dres := wizardAskLatest(memlatest, dep) if dres { dep.Reference = memlatest sv, err := semver.NewVersion(dep.Reference) if err == nil { res := wizardAskRange(sv, dep) if res == "m" { dep.Reference = "^" + sv.String() } else if res == "p" { dep.Reference = "~" + sv.String() } } } } } ================================================ FILE: action/get_test.go ================================================ package action import ( "io/ioutil" "testing" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" ) func TestAddPkgsToConfig(t *testing.T) { // Route output to discard so it's not displayed with the test output. o := msg.Default.Stderr msg.Default.Stderr = ioutil.Discard conf := new(cfg.Config) dep := new(cfg.Dependency) dep.Name = "github.com/Masterminds/cookoo" dep.Subpackages = append(dep.Subpackages, "convert") conf.Imports = append(conf.Imports, dep) names := []string{ "github.com/Masterminds/cookoo/fmt", "github.com/Masterminds/semver", } addPkgsToConfig(conf, names, false, true, false) if !conf.HasDependency("github.com/Masterminds/semver") { t.Error("addPkgsToConfig failed to add github.com/Masterminds/semver") } d := conf.Imports.Get("github.com/Masterminds/cookoo") found := false for _, s := range d.Subpackages { if s == "fmt" { found = true } } if !found { t.Error("addPkgsToConfig failed to add subpackage to existing import") } // Restore messaging to original location msg.Default.Stderr = o } ================================================ FILE: action/import_gb.go ================================================ package action import ( "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/gb" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" ) // ImportGB imports GB dependencies into the present glide config. func ImportGB(dest string) { base := "." config := EnsureConfig() if !gb.Has(base) { msg.Die("There is no GB manifest to import.") } deps, err := gb.Parse(base) if err != nil { msg.Die("Failed to extract GB manifest: %s", err) } appendImports(deps, config) writeConfigToFileOrStdout(config, dest) } func appendImports(deps []*cfg.Dependency, config *cfg.Config) { if len(deps) == 0 { msg.Info("No dependencies added.") return } //Append deps to existing dependencies. if err := config.AddImport(deps...); err != nil { msg.Die("Failed to add imports: %s", err) } } // writeConfigToFileOrStdout is a convenience function for import utils. func writeConfigToFileOrStdout(config *cfg.Config, dest string) { if dest != "" { if err := config.WriteFile(dest); err != nil { msg.Die("Failed to write %s: %s", gpath.GlideFile, err) } } else { o, err := config.Marshal() if err != nil { msg.Die("Error encoding config: %s", err) } msg.Default.Stdout.Write(o) } } ================================================ FILE: action/import_godep.go ================================================ package action import ( "github.com/Masterminds/glide/godep" "github.com/Masterminds/glide/msg" ) // ImportGodep imports a Godep file. func ImportGodep(dest string) { base := "." config := EnsureConfig() if !godep.Has(base) { msg.Die("No Godep data found.") } deps, err := godep.Parse(base) if err != nil { msg.Die("Failed to extract Godeps file: %s", err) } appendImports(deps, config) writeConfigToFileOrStdout(config, dest) } ================================================ FILE: action/import_gom.go ================================================ package action import ( "github.com/Masterminds/glide/gom" "github.com/Masterminds/glide/msg" ) // ImportGom imports a Gomfile. func ImportGom(dest string) { base := "." config := EnsureConfig() if !gom.Has(base) { msg.Die("No gom data found.") } deps, err := gom.Parse(base) if err != nil { msg.Die("Failed to extract Gomfile: %s", err) } appendImports(deps, config) writeConfigToFileOrStdout(config, dest) } ================================================ FILE: action/import_gpm.go ================================================ package action import ( "github.com/Masterminds/glide/gpm" "github.com/Masterminds/glide/msg" ) // ImportGPM imports a GPM file. func ImportGPM(dest string) { base := "." config := EnsureConfig() if !gpm.Has(base) { msg.Die("No GPM Godeps file found.") } deps, err := gpm.Parse(base) if err != nil { msg.Die("Failed to extract GPM Godeps file: %s", err) } appendImports(deps, config) writeConfigToFileOrStdout(config, dest) } ================================================ FILE: action/init.go ================================================ package action import ( gpath "github.com/Masterminds/glide/path" ) // Init initializes the action subsystem for handling one or more subesequent actions. func Init(yaml, home string) { gpath.GlideFile = yaml gpath.SetHome(home) } ================================================ FILE: action/install.go ================================================ package action import ( "path/filepath" "github.com/Masterminds/glide/cache" "github.com/Masterminds/glide/cfg" "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, stripVendor bool) { cache.SystemLock() base := "." // Ensure GOPATH EnsureGopath() EnsureVendorDir() conf := EnsureConfig() // Lockfile exists if !gpath.HasLock(base) { msg.Info("Lock file (glide.lock) does not exist. Performing update.") Update(installer, false, stripVendor) return } // Load lockfile lock, err := cfg.ReadLockFile(filepath.Join(base, gpath.LockFile)) if err != nil { msg.Die("Could not load lockfile.") } // Verify lockfile hasn't changed hash, err := conf.Hash() if err != nil { msg.Die("Could not load lockfile.") } else if hash != lock.Hash { msg.Warn("Lock file may be out of date. Hash check of YAML failed. You may need to run 'update'") } // Install newConf, err := installer.Install(lock, conf) if err != nil { msg.Die("Failed to install: %s", err) } msg.Info("Setting references.") // Set reference if err := repo.SetReference(newConf, installer.ResolveTest); err != nil { msg.Die("Failed to set references: %s (Skip to cleanup)", err) } err = installer.Export(newConf) if err != nil { msg.Die("Unable to export dependencies to vendor directory: %s", err) } if stripVendor { msg.Info("Removing nested vendor and Godeps/_workspace directories...") err := gpath.StripVendor() if err != nil { msg.Err("Unable to strip vendor directories: %s", err) } } } ================================================ FILE: action/list.go ================================================ package action import ( "encoding/json" "path/filepath" "sort" "github.com/Masterminds/glide/dependency" "github.com/Masterminds/glide/msg" ) // List lists all of the dependencies of the current project. // // Params: // - dir (string): basedir // - deep (bool): whether to do a deep scan or a shallow scan // - format (string): The format to output (text, json, json-pretty) func List(basedir string, deep bool, format string) { basedir, err := filepath.Abs(basedir) if err != nil { msg.Die("Could not read directory: %s", err) } r, err := dependency.NewResolver(basedir) if err != nil { msg.Die("Could not create a resolver: %s", err) } h := &dependency.DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}, Prefix: "vendor"} r.Handler = h localPkgs, _, err := r.ResolveLocal(deep) if err != nil { msg.Die("Error listing dependencies: %s", err) } sort.Strings(localPkgs) installed := make([]string, len(localPkgs)) for i, pkg := range localPkgs { relPkg, err := filepath.Rel(basedir, pkg) if err != nil { // msg.Warn("Failed to Rel path: %s", err) relPkg = pkg } installed[i] = relPkg } l := PackageList{ Installed: installed, Missing: h.Missing, Gopath: h.Gopath, } outputList(l, format) } // PackageList contains the packages being used by their location type PackageList struct { Installed []string `json:"installed"` Missing []string `json:"missing"` Gopath []string `json:"gopath"` } const ( textFormat = "text" jsonFormat = "json" jsonPrettyFormat = "json-pretty" ) func outputList(l PackageList, format string) { switch format { case textFormat: msg.Puts("INSTALLED packages:") for _, pkg := range l.Installed { msg.Puts("\t%s", pkg) } if len(l.Missing) > 0 { msg.Puts("\nMISSING packages:") for _, pkg := range l.Missing { msg.Puts("\t%s", pkg) } } if len(l.Gopath) > 0 { msg.Puts("\nGOPATH packages:") for _, pkg := range l.Gopath { msg.Puts("\t%s", pkg) } } case jsonFormat: json.NewEncoder(msg.Default.Stdout).Encode(l) case jsonPrettyFormat: b, err := json.MarshalIndent(l, "", " ") if err != nil { msg.Die("could not unmarshal package list: %s", err) } msg.Puts(string(b)) default: msg.Die("invalid output format: must be one of: json|json-pretty|text") } } ================================================ FILE: action/list_test.go ================================================ package action import ( "bytes" "encoding/json" "testing" "github.com/Masterminds/glide/msg" ) func TestList(t *testing.T) { msg.Default.PanicOnDie = true old := msg.Default.Stdout defer func() { msg.Default.Stdout = old }() var buf bytes.Buffer msg.Default.Stdout = &buf List("../", false, "text") if buf.Len() < 5 { t.Error("Expected some data to be found.") } var buf2 bytes.Buffer msg.Default.Stdout = &buf2 List("../", false, "json") j := buf2.Bytes() var o PackageList err := json.Unmarshal(j, &o) if err != nil { t.Errorf("Error unmarshaling json list: %s", err) } if len(o.Installed) == 0 { t.Error("No packages found on json list") } var buf3 bytes.Buffer msg.Default.Stdout = &buf3 List("../", false, "json-pretty") j = buf3.Bytes() var o2 PackageList err = json.Unmarshal(j, &o2) if err != nil { t.Errorf("Error unmarshaling json-pretty list: %s", err) } if len(o2.Installed) == 0 { t.Error("No packages found on json-pretty list") } } ================================================ FILE: action/mirrors.go ================================================ 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 } ================================================ FILE: action/name.go ================================================ package action import ( "github.com/Masterminds/glide/msg" ) // Name prints the name of the package, according to the glide.yaml file. func Name() { conf := EnsureConfig() msg.Puts(conf.Name) } ================================================ FILE: action/name_test.go ================================================ package action import ( "bytes" "os" "testing" "github.com/Masterminds/glide/msg" ) func TestName(t *testing.T) { var buf bytes.Buffer msg.Default.PanicOnDie = true ostdout := msg.Default.Stdout msg.Default.Stdout = &buf wd, _ := os.Getwd() if err := os.Chdir("../testdata/name"); err != nil { t.Errorf("Failed to change directory: %s", err) } Name() if buf.String() != "technosophos.com/x/foo\n" { t.Errorf("Unexpectedly got name %q", buf.String()) } msg.Default.Stdout = ostdout os.Chdir(wd) } ================================================ FILE: action/no_vendor.go ================================================ package action import ( "os" "path/filepath" "strings" "github.com/Masterminds/glide/msg" ) // NoVendor generates a list of source code directories, excepting `vendor/`. // // If "onlyGo" is true, only folders that have Go code in them will be returned. // // If suffix is true, this will append `/...` to every directory. func NoVendor(path string, onlyGo, suffix bool) { // This is responsible for printing the results of noVend. paths, err := noVend(path, onlyGo, suffix) if err != nil { msg.Err("Failed to walk file tree: %s", err) msg.Warn("FIXME: NoVendor should exit with non-zero exit code.") return } for _, p := range paths { msg.Puts(p) } } // noVend takes a directory and returns a list of Go-like files or directories, // provided the directory is not a vendor directory. // // If onlyGo is true, this will filter out all directories that do not contain // ".go" files. // // TODO: Should we move this to its own package? func noVend(path string, onlyGo, suffix bool) ([]string, error) { info, err := os.Stat(path) if err != nil { return []string{}, err } if !info.IsDir() { return []string{path}, nil } res := []string{} f, err := os.Open(path) if err != nil { return res, err } fis, err := f.Readdir(0) if err != nil { return res, err } cur := false for _, fi := range fis { if exclude(fi) { continue } full := filepath.Join(path, fi.Name()) if fi.IsDir() && !isVend(fi) { p := "./" + full + "/..." res = append(res, p) } else if !fi.IsDir() && isGoish(fi) { //res = append(res, full) cur = true } } // Filter out directories that do not contain Go code if onlyGo { res = hasGoSource(res, suffix) } if cur { res = append(res, ".") } return res, nil } // hasGoSource returns a list of directories that contain Go source. func hasGoSource(dirs []string, suffix bool) []string { suf := "/" if suffix { suf = "/..." } buf := []string{} for _, d := range dirs { d := filepath.Dir(d) found := false walker := func(p string, fi os.FileInfo, err error) error { // Dumb optimization if found { return nil } // If the file ends with .go, report a match. if strings.ToLower(filepath.Ext(p)) == ".go" { found = true } return nil } filepath.Walk(d, walker) if found { buf = append(buf, "./"+d+suf) } } return buf } // isVend returns true of this directory is a vendor directory. // // TODO: Should we return true for Godeps directory? func isVend(fi os.FileInfo) bool { return fi.Name() == "vendor" } // exclude returns true if the directory should be excluded by Go toolchain tools. // // Examples: directories prefixed with '.' or '_'. func exclude(fi os.FileInfo) bool { if strings.HasPrefix(fi.Name(), "_") { return true } if strings.HasPrefix(fi.Name(), ".") { return true } return false } // isGoish returns true if the file appears to be Go source. func isGoish(fi os.FileInfo) bool { return filepath.Ext(fi.Name()) == ".go" } ================================================ FILE: action/no_vendor_test.go ================================================ package action import ( "testing" "github.com/Masterminds/glide/msg" ) func TestNoVendor(t *testing.T) { msg.Default.PanicOnDie = true NoVendor("../testdata/nv", false, false) } ================================================ FILE: action/plugin.go ================================================ package action import ( "os" "os/exec" "github.com/Masterminds/glide/msg" ) // Plugin attempts to find and execute a plugin based on a command. // // Exit code 99 means the plugin was never executed. Code 1 means the program // exited badly. func Plugin(command string, args []string) { cwd, err := os.Getwd() if err != nil { msg.ExitCode(99) msg.Die("Could not get working directory: %s", err) } cmd := "glide-" + command var fullcmd string if fullcmd, err = exec.LookPath(cmd); err != nil { fullcmd = cwd + "/" + cmd if _, err := os.Stat(fullcmd); err != nil { msg.ExitCode(99) msg.Die("Command %s does not exist.", cmd) } } // Turning os.Args first argument from `glide` to `glide-command` args[0] = cmd // Removing the first argument (command) removed := false for i, v := range args { if removed == false && v == command { args = append(args[:i], args[i+1:]...) removed = true } } pa := os.ProcAttr{ Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, Dir: cwd, } msg.Debug("Delegating to plugin %s (%v)\n", fullcmd, args) proc, err := os.StartProcess(fullcmd, args, &pa) if err != nil { msg.Err("Failed to execute %s: %s", cmd, err) os.Exit(98) } if _, err := proc.Wait(); err != nil { msg.Err(err.Error()) os.Exit(1) } } ================================================ FILE: action/plugin_test.go ================================================ package action import ( "os" "runtime" "testing" "github.com/Masterminds/glide/msg" ) func TestPlugin(t *testing.T) { wd, _ := os.Getwd() os.Chdir("../testdata/plugin") msg.Default.PanicOnDie = true var cmd string // Windows scripts for testing (batch) are different from shells scripts. // Making sure the plugin works in both bases. if runtime.GOOS == "windows" { cmd = "hello-win" } else { cmd = "hello" } args := []string{"a", "b"} // FIXME: Trapping the panic is the nice thing to do. Plugin(cmd, args) os.Chdir(wd) } ================================================ FILE: action/project_info.go ================================================ package action import ( "bytes" "github.com/Masterminds/glide/msg" ) // Info prints information about a project based on a passed in format. func Info(format string) { conf := EnsureConfig() var buffer bytes.Buffer varInit := false for _, varfmt := range format { if varInit { switch varfmt { case 'n': buffer.WriteString(conf.Name) case 'd': buffer.WriteString(conf.Description) case 'h': buffer.WriteString(conf.Home) case 'l': buffer.WriteString(conf.License) default: msg.Die("Invalid format %s", string(varfmt)) } } else { switch varfmt { case '%': varInit = true continue default: buffer.WriteString(string(varfmt)) } } varInit = false } msg.Puts(buffer.String()) } ================================================ FILE: action/rebuild.go ================================================ package action import ( "os" "os/exec" "path" "path/filepath" "strings" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" ) // Rebuild rebuilds '.a' files for a project. // // Prior to Go 1.4, this could substantially reduce time on incremental compiles. // It remains to be seen whether this is tremendously beneficial to modern Go // programs. func Rebuild() { msg.Warn("The rebuild command is deprecated and will be removed in a future version") msg.Warn("Use the go install command instead") conf := EnsureConfig() vpath, err := gpath.Vendor() if err != nil { msg.Die("Could not get vendor path: %s", err) } msg.Info("Building dependencies.\n") if len(conf.Imports) == 0 { msg.Info("No dependencies found. Nothing built.\n") return } for _, dep := range conf.Imports { if err := buildDep(dep, vpath); err != nil { msg.Warn("Failed to build %s: %s\n", dep.Name, err) } } } func buildDep(dep *cfg.Dependency, vpath string) error { if len(dep.Subpackages) == 0 { buildPath(dep.Name) } for _, pkg := range dep.Subpackages { if pkg == "**" || pkg == "..." { //Info("Building all packages in %s\n", dep.Name) buildPath(path.Join(dep.Name, "...")) } else { paths, err := resolvePackages(vpath, dep.Name, pkg) if err != nil { msg.Warn("Error resolving packages: %s", err) } buildPaths(paths) } } return nil } func resolvePackages(vpath, pkg, subpkg string) ([]string, error) { sdir, _ := os.Getwd() if err := os.Chdir(filepath.Join(vpath, pkg, subpkg)); err != nil { return []string{}, err } defer os.Chdir(sdir) p, err := filepath.Glob(path.Join(vpath, pkg, subpkg)) if err != nil { return []string{}, err } for k, v := range p { nv := strings.TrimPrefix(v, vpath) p[k] = strings.TrimPrefix(nv, string(filepath.Separator)) } return p, nil } func buildPaths(paths []string) error { for _, path := range paths { if err := buildPath(path); err != nil { return err } } return nil } func buildPath(path string) error { msg.Info("Running go build %s\n", path) // . in a filepath.Join is removed so it needs to be prepended separately. p := "." + string(filepath.Separator) + filepath.Join("vendor", path) out, err := exec.Command(goExecutable(), "install", p).CombinedOutput() if err != nil { msg.Warn("Failed to run 'go install' for %s: %s", path, string(out)) } return err } ================================================ FILE: action/rebuild_test.go ================================================ package action import ( "os" "testing" "github.com/Masterminds/glide/msg" ) func TestRebuild(t *testing.T) { msg.Default.PanicOnDie = true wd, _ := os.Getwd() if err := os.Chdir("../testdata/rebuild"); err != nil { t.Errorf("Could not change dir: %s (%s)", err, wd) } Rebuild() os.Chdir(wd) } ================================================ FILE: action/remove.go ================================================ package action import ( "github.com/Masterminds/glide/cache" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/glide/repo" ) // Remove removes a dependncy from the configuration. func Remove(packages []string, inst *repo.Installer) { cache.SystemLock() base := gpath.Basepath() EnsureGopath() EnsureVendorDir() conf := EnsureConfig() glidefile, err := gpath.Glide() if err != nil { msg.Die("Could not find Glide file: %s", err) } msg.Info("Preparing to remove %d packages.", len(packages)) conf.Imports = rmDeps(packages, conf.Imports) conf.DevImports = rmDeps(packages, conf.DevImports) // Copy used to generate locks. confcopy := conf.Clone() //confcopy.Imports = inst.List(confcopy) if err := repo.SetReference(confcopy, inst.ResolveTest); err != nil { msg.Err("Failed to set references: %s", err) } err = inst.Export(confcopy) if err != nil { msg.Die("Unable to export dependencies to vendor directory: %s", err) } // Write glide.yaml if err := conf.WriteFile(glidefile); err != nil { msg.Die("Failed to write glide YAML file: %s", err) } // Write glide lock writeLock(conf, confcopy, base) } // rmDeps returns a list of dependencies that do not contain the given pkgs. // // It generates neither an error nor a warning for a pkg that does not exist // in the list of deps. func rmDeps(pkgs []string, deps []*cfg.Dependency) []*cfg.Dependency { res := []*cfg.Dependency{} for _, d := range deps { rem := false for _, p := range pkgs { if p == d.Name { rem = true } } if !rem { res = append(res, d) } } return res } ================================================ FILE: action/tree.go ================================================ package action import ( "container/list" "os" "github.com/Masterminds/glide/msg" "github.com/Masterminds/glide/tree" "github.com/Masterminds/glide/util" ) // Tree prints a tree representing dependencies. func Tree(basedir string, showcore bool) { msg.Warn("The tree command is deprecated and will be removed in a future version") buildContext, err := util.GetBuildContext() if err != nil { msg.Die("Failed to get a build context: %s", err) } myName := buildContext.PackageName(basedir) if basedir == "." { var err error basedir, err = os.Getwd() if err != nil { msg.Die("Could not get working directory") } } msg.Puts(myName) l := list.New() l.PushBack(myName) tree.Display(buildContext, basedir, myName, 1, showcore, l) } ================================================ FILE: action/update.go ================================================ package action import ( "io/ioutil" "path/filepath" "github.com/Masterminds/glide/cache" "github.com/Masterminds/glide/cfg" "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, stripVendor bool) { cache.SystemLock() base := "." EnsureGopath() EnsureVendorDir() conf := EnsureConfig() // 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) } // Set the versions for the initial dependencies so that resolved dependencies // are rooted in the correct version of the base. if err := repo.SetReference(conf, installer.ResolveTest); err != nil { msg.Die("Failed to set initial config references: %s", err) } // Prior to resolving dependencies we need to start working with a clone // of the conf because we'll be making real changes to it. confcopy := conf.Clone() if !skipRecursive { // Get all repos and update them. err := installer.Update(confcopy) if err != nil { msg.Die("Could not update packages: %s", err) } // Set references. There may be no remaining references to set since the // installer set them as it went to make sure it parsed the right imports // from the right version of the package. msg.Info("Setting references for remaining imports") if err := repo.SetReference(confcopy, installer.ResolveTest); err != nil { msg.Err("Failed to set references: %s (Skip to cleanup)", err) } } 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?) // I think we don't need to write a new Glide file because update should not // change anything important. It will just generate information about // transative dependencies, all of which belongs exclusively in the lock // file, not the glide.yaml file. // TODO(mattfarina): Detect when a new dependency has been added or removed // from the project. A removed dependency should warn and an added dependency // should be added to the glide.yaml file. See issue #193. if !skipRecursive { // Write lock hash, err := conf.Hash() if err != nil { msg.Die("Failed to generate config hash. Unable to generate lock file.") } 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)) if err == nil { l2, err := cfg.LockfileFromYaml(yml) if err == nil { f1, err := l2.Fingerprint() f2, err2 := lock.Fingerprint() if err == nil && err2 == nil && f1 == f2 { wl = false } } } } if wl { if err := lock.WriteFile(filepath.Join(base, gpath.LockFile)); err != nil { msg.Err("Could not write lock file to %s: %s", base, err) return } } else { msg.Info("Versions did not change. Skipping glide.lock update.") } msg.Info("Project relies on %d dependencies.", len(confcopy.Imports)) } else { msg.Warn("Skipping lockfile generation because full dependency tree is not being calculated") } if stripVendor { msg.Info("Removing nested vendor and Godeps/_workspace directories...") err := gpath.StripVendor() if err != nil { msg.Err("Unable to strip vendor directories: %s", err) } } } ================================================ FILE: appveyor.yml ================================================ version: build-{build}.{branch} clone_folder: C:\gopath\src\github.com\Masterminds\glide shallow_clone: true environment: GOPATH: C:\gopath platform: - x64 build: off install: - go version - go env test_script: - go test -v . ./gb ./path ./action ./tree ./util ./godep ./godep/strip ./gpm ./cfg ./dependency ./importer ./msg ./repo ./mirrors deploy: off ================================================ FILE: cache/cache.go ================================================ // Package cache provides an interface for interfacing with the Glide local cache // // Glide has a local cache of metadata and repositories similar to the GOPATH. // To store the cache Glide creates a .glide directory with a cache subdirectory. // This is usually in the users home directory unless there is no accessible // home directory in which case the .glide directory is in the root of the // repository. // // To get the cache location use the `cache.Location()` function. This will // return the proper base location in your environment. // // Within the cache directory there are two subdirectories. They are the src // and info directories. The src directory contains version control checkouts // of the packages. The info direcory contains metadata. The metadata maps to // the RepoInfo struct. Both stores are happed to keys. // // Using the `cache.Key()` function you can get a key for a repo. Pass in a // location such as `https://github.com/foo/bar` or `git@example.com:foo.git` // and a key will be returned that can be used for caching operations. // // Note, the caching is based on repo rather than package. This is important // for a couple reasons. // // 1. Forks or package replacements are supported in Glide. Where a different // repo maps to a package. // 2. Permissions enable different access. For example `https://example.com/foo.git` // and `git@example.com:foo.git` may have access to different branches or tags. package cache import ( "encoding/json" "errors" "io/ioutil" "net/url" "os" "path/filepath" "regexp" "strings" "sync" "time" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" ) // Enabled sets if the cache is globally enabled. Defaults to true. var Enabled = true // ErrCacheDisabled is returned with the cache is disabled. var ErrCacheDisabled = errors.New("Cache disabled") var isSetup bool var setupMutex sync.Mutex // Setup creates the cache location. func Setup() { setupMutex.Lock() defer setupMutex.Unlock() if isSetup { return } msg.Debug("Setting up the cache directory") pths := []string{ "cache", filepath.Join("cache", "src"), filepath.Join("cache", "info"), } for _, l := range pths { err := os.MkdirAll(filepath.Join(gpath.Home(), l), 0755) if err != nil { msg.Die("Cache directory unavailable: %s", err) } } isSetup = true } // SetupReset resets if setup has been completed. The next time setup is run // it will attempt a full setup. func SetupReset() { isSetup = false } // Location returns the location of the cache. func Location() string { p := filepath.Join(gpath.Home(), "cache") Setup() return p } // scpSyntaxRe matches the SCP-like addresses used to access repos over SSH. var scpSyntaxRe = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) // Key generates a cache key based on a url or scp string. The key is file // system safe. func Key(repo string) (string, error) { var u *url.URL var err error var strip bool if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil { // Match SCP-like syntax and convert it to a URL. // Eg, "git@github.com:user/repo" becomes // "ssh://git@github.com/user/repo". u = &url.URL{ Scheme: "ssh", User: url.User(m[1]), Host: m[2], Path: "/" + m[3], } strip = true } else { u, err = url.Parse(repo) if err != nil { return "", err } } if strip { u.Scheme = "" } var key string if u.Scheme != "" { key = u.Scheme + "-" } if u.User != nil && u.User.Username() != "" { key = key + u.User.Username() + "-" } key = key + u.Host if u.Path != "" { key = key + strings.Replace(u.Path, "/", "-", -1) } key = strings.Replace(key, ":", "-", -1) return key, nil } // RepoInfo holds information about a repo. type RepoInfo struct { DefaultBranch string `json:"default-branch"` LastUpdate string `json:"last-update"` } // SaveRepoData stores data about a repo in the Glide cache func SaveRepoData(key string, data RepoInfo) error { if !Enabled { return ErrCacheDisabled } location := Location() data.LastUpdate = time.Now().String() d, err := json.Marshal(data) if err != nil { return err } pp := filepath.Join(location, "info") err = os.MkdirAll(pp, 0755) if err != nil { return err } p := filepath.Join(pp, key+".json") f, err := os.Create(p) if err != nil { return err } defer f.Close() _, err = f.Write(d) return err } // RepoData retrieves cached information about a repo. func RepoData(key string) (*RepoInfo, error) { if !Enabled { return &RepoInfo{}, ErrCacheDisabled } location := Location() c := &RepoInfo{} p := filepath.Join(location, "info", key+".json") f, err := ioutil.ReadFile(p) if err != nil { return &RepoInfo{}, err } err = json.Unmarshal(f, c) if err != nil { return &RepoInfo{}, err } return c, nil } var lockSync sync.Mutex var lockData = make(map[string]*sync.Mutex) // Lock locks a particular key name func Lock(name string) { lockSync.Lock() m, ok := lockData[name] if !ok { m = &sync.Mutex{} lockData[name] = m } lockSync.Unlock() msg.Debug("Locking %s", name) m.Lock() } // Unlock unlocks a particular key name func Unlock(name string) { msg.Debug("Unlocking %s", name) lockSync.Lock() if m, ok := lockData[name]; ok { m.Unlock() } lockSync.Unlock() } ================================================ FILE: cache/cache_test.go ================================================ package cache import "testing" func TestKey(t *testing.T) { tests := map[string]string{ "https://github.com/foo/bar": "https-github.com-foo-bar", "git@github.com:foo/bar": "git-github.com-foo-bar", "https://github.com:123/foo/bar": "https-github.com-123-foo-bar", } for k, v := range tests { key, err := Key(k) if err != nil { t.Errorf("Cache key generation err: %s", err) continue } if key != v { t.Errorf("Expected cache key %s for %s but got %s", v, k, key) } } } ================================================ FILE: cache/global_lock.go ================================================ package cache import ( "encoding/json" "io/ioutil" "os" "os/signal" "path/filepath" "time" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" ) var isStarted bool // If the global cache lock file should be written var shouldWriteLock = true // SystemLock starts a system rather than application lock. This way multiple // app instances don't cause race conditions when working in the cache. func SystemLock() error { if isStarted { return nil } err := waitOnLock() if err != nil { return err } err = startLock() isStarted = true return err } // SystemUnlock removes the system wide Glide cache lock. func SystemUnlock() { lockdone <- struct{}{} os.Remove(lockFileName) } var lockdone = make(chan struct{}, 1) type lockdata struct { Comment string `json:"comment"` Pid int `json:"pid"` Time string `json:"time"` } var lockFileName = filepath.Join(gpath.Home(), "lock.json") // Write a lock for now. func writeLock() error { // If the lock should not be written exit immediately. This happens in cases // where shutdown/clean is happening. if !shouldWriteLock { return nil } ld := &lockdata{ Comment: "File managed by Glide (https://glide.sh)", Pid: os.Getpid(), Time: time.Now().Format(time.RFC3339Nano), } out, err := json.Marshal(ld) if err != nil { return err } err = ioutil.WriteFile(lockFileName, out, 0755) return err } func startLock() error { err := writeLock() if err != nil { return err } go func() { for { select { case <-lockdone: return default: time.Sleep(10 * time.Second) err := writeLock() if err != nil { msg.Die("Error using Glide lock: %s", err) } } } }() // Capture ctrl-c or other interruptions then clean up the global lock. ch := make(chan os.Signal) signal.Notify(ch, os.Interrupt, os.Kill) go func(cc <-chan os.Signal) { s := <-cc shouldWriteLock = false SystemUnlock() // Exiting with the expected exit codes when we can. if s == os.Interrupt { os.Exit(130) } else if s == os.Kill { os.Exit(137) } else { os.Exit(1) } }(ch) return nil } func waitOnLock() error { var announced bool for { fi, err := os.Stat(lockFileName) if err != nil && os.IsNotExist(err) { return nil } else if err != nil { return err } diff := time.Now().Sub(fi.ModTime()) if diff.Seconds() > 15 { return nil } if !announced { announced = true msg.Info("Waiting on Glide global cache access") } // Check on the lock file every second. time.Sleep(time.Second) } } ================================================ FILE: cache/memory.go ================================================ package cache import ( "sync" "github.com/Masterminds/glide/msg" "github.com/Masterminds/semver" ) // Provide an in memory cache of imported project information. var defaultMemCache = newMemCache() // MemPut put a version into the in memory cache for a name. // This will silently ignore non-semver and make sure the latest // is stored. func MemPut(name, version string) { defaultMemCache.put(name, version) } // MemTouched returns true if the cache was touched for a name. func MemTouched(name string) bool { return defaultMemCache.touched(name) } // MemTouch notes if a name has been looked at. func MemTouch(name string) { defaultMemCache.touch(name) } // MemLatest returns the latest, that is most recent, semver release. This // may be a blank string if no put value func MemLatest(name string) string { return defaultMemCache.getLatest(name) } // MemSetCurrent is used to set the current version in use. func MemSetCurrent(name, version string) { defaultMemCache.setCurrent(name, version) } // MemCurrent is used to get the current version in use. func MemCurrent(name string) string { return defaultMemCache.current(name) } // An in memory cache. type memCache struct { sync.RWMutex latest map[string]string t map[string]bool versions map[string][]string c map[string]string } func newMemCache() *memCache { return &memCache{ latest: make(map[string]string), t: make(map[string]bool), versions: make(map[string][]string), c: make(map[string]string), } } func (m *memCache) setCurrent(name, version string) { m.Lock() defer m.Unlock() if m.c[name] == "" { m.c[name] = version } else { // If we already have a version try to see if the new or old one is // semver and use that one. _, err := semver.NewVersion(m.c[name]) if err != nil { _, err2 := semver.NewVersion(version) if err2 == nil { m.c[name] = version } } } } func (m *memCache) current(name string) string { m.RLock() defer m.RUnlock() return m.c[name] } func (m *memCache) put(name, version string) { m.Lock() defer m.Unlock() m.t[name] = true sv, err := semver.NewVersion(version) if err != nil { msg.Debug("Ignoring %s version %s: %s", name, version, err) return } latest, found := m.latest[name] if found { lv, err := semver.NewVersion(latest) if err == nil { if sv.GreaterThan(lv) { m.latest[name] = version } } } else { m.latest[name] = version } found = false for _, v := range m.versions[name] { if v == version { found = true } } if !found { m.versions[name] = append(m.versions[name], version) } } func (m *memCache) touch(name string) { m.Lock() defer m.Unlock() m.t[name] = true } func (m *memCache) touched(name string) bool { m.RLock() defer m.RUnlock() return m.t[name] } func (m *memCache) getLatest(name string) string { m.RLock() defer m.RUnlock() return m.latest[name] } ================================================ FILE: cfg/cfg.go ================================================ // Package cfg handles working with the Glide configuration files. // // The cfg package contains the ability to parse (unmarshal) and write (marshal) // glide.yaml and glide.lock files. These files contains the details about // projects managed by Glide. // // To convert yaml into a cfg.Config instance use the cfg.ConfigFromYaml function. // The yaml, typically in a glide.yaml file, has the following structure. // // package: github.com/Masterminds/glide // homepage: https://masterminds.github.io/glide // license: MIT // owners: // - name: Matt Butcher // email: technosophos@gmail.com // homepage: http://technosophos.com // - name: Matt Farina // email: matt@mattfarina.com // homepage: https://www.mattfarina.com // ignore: // - appengine // excludeDirs: // - node_modules // import: // - package: gopkg.in/yaml.v2 // - package: github.com/Masterminds/vcs // version: ^1.2.0 // repo: git@github.com:Masterminds/vcs // vcs: git // - package: github.com/codegangsta/cli // - package: github.com/Masterminds/semver // version: ^1.0.0 // // These elements are: // // - package: The top level package is the location in the GOPATH. This is used // for things such as making sure an import isn't also importing the top level // package. // - homepage: To find the place where you can find details about the package or // applications. For example, http://k8s.io // - license: The license is either an SPDX license string or the filepath to the // license. This allows automation and consumers to easily identify the license. // - owners: The owners is a list of one or more owners for the project. This // can be a person or organization and is useful for things like notifying the // owners of a security issue without filing a public bug. // - ignore: A list of packages for Glide to ignore importing. These are package // names to ignore rather than directories. // - excludeDirs: A list of directories in the local codebase to exclude from // scanning for dependencies. // - import: A list of packages to import. Each package can include: // - package: The name of the package to import and the only non-optional item. // - version: A semantic version, semantic version range, branch, tag, or // commit id to use. // - repo: If the package name isn't the repo location or this is a private // repository it can go here. The package will be checked out from the // repo and put where the package name specifies. This allows using forks. // - vcs: A VCS to use such as git, hg, bzr, or svn. This is only needed // when the type cannot be detected from the name. For example, a repo // ending in .git or on GitHub can be detected to be Git. For a repo on // Bitbucket we can contact the API to discover the type. // - testImport: A list of development packages not already listed under import. // Each package has the same details as those listed under import. package cfg ================================================ FILE: cfg/config.go ================================================ package cfg import ( "crypto/sha256" "fmt" "io/ioutil" "reflect" "sort" "strings" "github.com/Masterminds/glide/mirrors" "github.com/Masterminds/glide/util" "github.com/Masterminds/vcs" "gopkg.in/yaml.v2" ) // Config is the top-level configuration object. type Config struct { // Name is the name of the package or application. Name string `yaml:"package"` // Description is a short description for a package, application, or library. // This description is similar but different to a Go package description as // it is for marketing and presentation purposes rather than technical ones. Description string `json:"description,omitempty"` // Home is a url to a website for the package. Home string `yaml:"homepage,omitempty"` // License provides either a SPDX license or a path to a file containing // the license. For more information on SPDX see http://spdx.org/licenses/. // When more than one license an SPDX expression can be used. License string `yaml:"license,omitempty"` // Owners is an array of owners for a project. See the Owner type for // more detail. These can be one or more people, companies, or other // organizations. Owners Owners `yaml:"owners,omitempty"` // Ignore contains a list of packages to ignore fetching. This is useful // when walking the package tree (including packages of packages) to list // those to skip. Ignore []string `yaml:"ignore,omitempty"` // Exclude contains a list of directories in the local application to // exclude from scanning for dependencies. Exclude []string `yaml:"excludeDirs,omitempty"` // Imports contains a list of all non-development imports for a project. For // more detail on how these are captured see the Dependency type. Imports Dependencies `yaml:"import"` // DevImports contains the test or other development imports for a project. // See the Dependency type for more details on how this is recorded. DevImports Dependencies `yaml:"testImport,omitempty"` } // A transitive representation of a dependency for importing and exporting to yaml. type cf struct { Name string `yaml:"package"` Description string `yaml:"description,omitempty"` Home string `yaml:"homepage,omitempty"` License string `yaml:"license,omitempty"` Owners Owners `yaml:"owners,omitempty"` Ignore []string `yaml:"ignore,omitempty"` Exclude []string `yaml:"excludeDirs,omitempty"` Imports Dependencies `yaml:"import"` DevImports Dependencies `yaml:"testImport,omitempty"` } // ConfigFromYaml returns an instance of Config from YAML func ConfigFromYaml(yml []byte) (*Config, error) { cfg := &Config{} err := yaml.Unmarshal([]byte(yml), &cfg) return cfg, err } // Marshal converts a Config instance to YAML func (c *Config) Marshal() ([]byte, error) { yml, err := yaml.Marshal(&c) if err != nil { return []byte{}, err } return yml, nil } // UnmarshalYAML is a hook for gopkg.in/yaml.v2 in the unmarshalling process func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { newConfig := &cf{} if err := unmarshal(&newConfig); err != nil { return err } c.Name = newConfig.Name c.Description = newConfig.Description c.Home = newConfig.Home c.License = newConfig.License c.Owners = newConfig.Owners c.Ignore = newConfig.Ignore c.Exclude = newConfig.Exclude c.Imports = newConfig.Imports c.DevImports = newConfig.DevImports // Cleanup the Config object now that we have it. err := c.DeDupe() return err } // MarshalYAML is a hook for gopkg.in/yaml.v2 in the marshaling process func (c *Config) MarshalYAML() (interface{}, error) { newConfig := &cf{ Name: c.Name, Description: c.Description, Home: c.Home, License: c.License, Owners: c.Owners, Ignore: c.Ignore, Exclude: c.Exclude, } i, err := c.Imports.Clone().DeDupe() if err != nil { return newConfig, err } di, err := c.DevImports.Clone().DeDupe() if err != nil { return newConfig, err } newConfig.Imports = i newConfig.DevImports = di return newConfig, nil } // HasDependency returns true if the given name is listed as an import or dev import. func (c *Config) HasDependency(name string) bool { for _, d := range c.Imports { if d.Name == name { return true } } for _, d := range c.DevImports { if d.Name == name { return true } } return false } // HasIgnore returns true if the given name is listed on the ignore list. func (c *Config) HasIgnore(name string) bool { for _, v := range c.Ignore { // Check for both a name and to make sure sub-packages are ignored as // well. if v == name || strings.HasPrefix(name, v+"/") { return true } } return false } // HasExclude returns true if the given name is listed on the exclude list. func (c *Config) HasExclude(ex string) bool { ep := normalizeSlash(ex) for _, v := range c.Exclude { if vp := normalizeSlash(v); vp == ep { return true } } return false } // Clone performs a deep clone of the Config instance func (c *Config) Clone() *Config { n := &Config{} n.Name = c.Name n.Description = c.Description n.Home = c.Home n.License = c.License n.Owners = c.Owners.Clone() n.Ignore = c.Ignore n.Exclude = c.Exclude n.Imports = c.Imports.Clone() n.DevImports = c.DevImports.Clone() return n } // WriteFile writes a Glide 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 (c *Config) WriteFile(glidepath string) error { o, err := c.Marshal() if err != nil { return err } return ioutil.WriteFile(glidepath, o, 0666) } // DeDupe consolidates duplicate dependencies on a Config instance func (c *Config) DeDupe() error { // Remove duplicates in the imports var err error c.Imports, err = c.Imports.DeDupe() if err != nil { return err } c.DevImports, err = c.DevImports.DeDupe() if err != nil { return err } // If the name on the config object is part of the imports remove it. found := -1 for i, dep := range c.Imports { if dep.Name == c.Name { found = i } } if found >= 0 { c.Imports = append(c.Imports[:found], c.Imports[found+1:]...) } found = -1 for i, dep := range c.DevImports { if dep.Name == c.Name { found = i } } if found >= 0 { c.DevImports = append(c.DevImports[:found], c.DevImports[found+1:]...) } // If something is on the ignore list remove it from the imports. for _, v := range c.Ignore { found = -1 for k, d := range c.Imports { if v == d.Name { found = k } } if found >= 0 { c.Imports = append(c.Imports[:found], c.Imports[found+1:]...) } found = -1 for k, d := range c.DevImports { if v == d.Name { found = k } } if found >= 0 { c.DevImports = append(c.DevImports[:found], c.DevImports[found+1:]...) } } return nil } // AddImport appends dependencies to the import list, deduplicating as we go. func (c *Config) AddImport(deps ...*Dependency) error { t := c.Imports t = append(t, deps...) t, err := t.DeDupe() if err != nil { return err } c.Imports = t return nil } // Hash generates a sha256 hash for a given Config func (c *Config) Hash() (string, error) { yml, err := c.Marshal() if err != nil { return "", err } hash := sha256.New() hash.Write(yml) return fmt.Sprintf("%x", hash.Sum(nil)), nil } // Dependencies is a collection of Dependency type Dependencies []*Dependency // Get a dependency by name func (d Dependencies) Get(name string) *Dependency { for _, dep := range d { if dep.Name == name { return dep } } return nil } // Has checks if a dependency is on a list of dependencies such as import or testImport func (d Dependencies) Has(name string) bool { for _, dep := range d { if dep.Name == name { return true } } return false } // Remove removes a dependency from a list of dependencies func (d Dependencies) Remove(name string) Dependencies { found := -1 for i, dep := range d { if dep.Name == name { found = i } } if found >= 0 { copy(d[found:], d[found+1:]) d[len(d)-1] = nil return d[:len(d)-1] } return d } // Clone performs a deep clone of Dependencies func (d Dependencies) Clone() Dependencies { n := make(Dependencies, 0, len(d)) for _, v := range d { n = append(n, v.Clone()) } return n } // DeDupe cleans up duplicates on a list of dependencies. func (d Dependencies) DeDupe() (Dependencies, error) { checked := map[string]int{} imports := make(Dependencies, 0, 1) i := 0 for _, dep := range d { // The first time we encounter a dependency add it to the list if val, ok := checked[dep.Name]; !ok { checked[dep.Name] = i imports = append(imports, dep) i++ } else { // In here we've encountered a dependency for the second time. // Make sure the details are the same or return an error. v := imports[val] if dep.Reference != v.Reference { return d, fmt.Errorf("Import %s repeated with different versions '%s' and '%s'", dep.Name, dep.Reference, v.Reference) } if dep.Repository != v.Repository || dep.VcsType != v.VcsType { return d, fmt.Errorf("Import %s repeated with different Repository details", dep.Name) } if !reflect.DeepEqual(dep.Os, v.Os) || !reflect.DeepEqual(dep.Arch, v.Arch) { return d, fmt.Errorf("Import %s repeated with different OS or Architecture filtering", dep.Name) } imports[checked[dep.Name]].Subpackages = stringArrayDeDupe(v.Subpackages, dep.Subpackages...) } } return imports, nil } // 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"` } // A transitive representation of a dependency for importing and exploting to yaml. type dep struct { Name string `yaml:"package"` Reference string `yaml:"version,omitempty"` Ref string `yaml:"ref,omitempty"` 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"` } // DependencyFromLock converts a Lock to a Dependency func DependencyFromLock(lock *Lock) *Dependency { return &Dependency{ Name: lock.Name, Reference: lock.Version, Repository: lock.Repository, VcsType: lock.VcsType, Subpackages: lock.Subpackages, Arch: lock.Arch, Os: lock.Os, } } // UnmarshalYAML is a hook for gopkg.in/yaml.v2 in the unmarshaling process func (d *Dependency) UnmarshalYAML(unmarshal func(interface{}) error) error { newDep := &dep{} err := unmarshal(&newDep) if err != nil { return err } d.Name = newDep.Name d.Reference = newDep.Reference d.Repository = newDep.Repository d.VcsType = newDep.VcsType d.Subpackages = newDep.Subpackages d.Arch = newDep.Arch d.Os = newDep.Os if d.Reference == "" && newDep.Ref != "" { d.Reference = newDep.Ref } // Make sure only legitimate VCS are listed. d.VcsType = filterVcsType(d.VcsType) // Get the root name for the package tn, subpkg := util.NormalizeName(d.Name) d.Name = tn if subpkg != "" { d.Subpackages = append(d.Subpackages, subpkg) } // Older versions of Glide had a / prefix on subpackages in some cases. // Here that's cleaned up. Someday we should be able to remove this. for k, v := range d.Subpackages { d.Subpackages[k] = strings.TrimPrefix(v, "/") } return nil } // MarshalYAML is a hook for gopkg.in/yaml.v2 in the marshaling process func (d *Dependency) MarshalYAML() (interface{}, error) { // Make sure we only write the correct vcs type to file t := filterVcsType(d.VcsType) newDep := &dep{ Name: d.Name, Reference: d.Reference, Repository: d.Repository, VcsType: t, Subpackages: d.Subpackages, Arch: d.Arch, Os: d.Os, } 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. remote := d.Remote() VcsType := d.Vcs() // If the VCS type has a value we try that first. if len(VcsType) > 0 && VcsType != "None" { switch vcs.Type(VcsType) { case vcs.Git: return vcs.NewGitRepo(remote, dest) case vcs.Svn: return vcs.NewSvnRepo(remote, dest) case vcs.Hg: return vcs.NewHgRepo(remote, dest) case vcs.Bzr: return vcs.NewBzrRepo(remote, dest) default: return nil, fmt.Errorf("Unknown VCS type %s set for %s", VcsType, d.Name) } } // When no type set we try to autodetect. return vcs.NewRepo(remote, dest) } // 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, } } // HasSubpackage returns if the subpackage is present on the dependency func (d *Dependency) HasSubpackage(sub string) bool { for _, v := range d.Subpackages { if sub == v { return true } } return false } // Owners is a list of owners for a project. type Owners []*Owner // Clone performs a deep clone of Owners func (o Owners) Clone() Owners { n := make(Owners, 0, 1) for _, v := range o { n = append(n, v.Clone()) } return n } // Owner describes an owner of a package. This can be a person, company, or // other organization. This is useful if someone needs to contact the // owner of a package to address things like a security issue. type Owner struct { // Name describes the name of an organization. Name string `yaml:"name,omitempty"` // Email is an email address to reach the owner at. Email string `yaml:"email,omitempty"` // Home is a url to a website for the owner. Home string `yaml:"homepage,omitempty"` } // Clone creates a clone of a Dependency func (o *Owner) Clone() *Owner { return &Owner{ Name: o.Name, Email: o.Email, Home: o.Home, } } func stringArrayDeDupe(s []string, items ...string) []string { for _, item := range items { exists := false for _, v := range s { if v == item { exists = true } } if !exists { s = append(s, item) } } sort.Strings(s) return s } func filterVcsType(vcs string) string { switch vcs { case "git", "hg", "bzr", "svn": return vcs case "mercurial": return "hg" case "bazaar": return "bzr" case "subversion": return "svn" default: return "" } } func normalizeSlash(k string) string { return strings.Replace(k, "\\", "/", -1) } ================================================ FILE: cfg/config_test.go ================================================ package cfg import ( "testing" "gopkg.in/yaml.v2" ) var yml = ` package: fake/testing description: foo bar baz homepage: https://example.com license: MIT owners: - name: foo email: bar@example.com homepage: https://example.com import: - package: github.com/kylelemons/go-gypsy subpackages: - yaml # Intentionally left spaces at end of next line. - package: github.com/Masterminds/convert repo: git@github.com:Masterminds/convert.git vcs: git ref: a9949121a2e2192ca92fa6dddfeaaa4a4412d955 subpackages: - color - nautical - radial os: - linux arch: - i386 - arm - package: github.com/Masterminds/structable - package: github.com/Masterminds/cookoo/color - package: github.com/Masterminds/cookoo/convert testImport: - package: github.com/kylelemons/go-gypsy ` func TestManualConfigFromYaml(t *testing.T) { cfg := &Config{} err := yaml.Unmarshal([]byte(yml), &cfg) if err != nil { t.Errorf("Unable to Unmarshal config yaml") } if cfg.Name != "fake/testing" { t.Errorf("Inaccurate name found %s", cfg.Name) } if cfg.Description != "foo bar baz" { t.Errorf("Inaccurate description found %s", cfg.Description) } if cfg.Home != "https://example.com" { t.Errorf("Inaccurate homepage found %s", cfg.Home) } if cfg.License != "MIT" { t.Errorf("Inaccurate license found %s", cfg.License) } found := false found2 := false for _, i := range cfg.Imports { if i.Name == "github.com/Masterminds/convert" { found = true ref := "a9949121a2e2192ca92fa6dddfeaaa4a4412d955" if i.Reference != ref { t.Errorf("Config reference for cookoo is inaccurate. Expected '%s' found '%s'", ref, i.Reference) } } if i.Name == "github.com/Masterminds/cookoo" { found2 = true if i.Subpackages[0] != "color" { t.Error("Dependency separating package and subpackage not working") } } } if !found { t.Error("Unable to find github.com/Masterminds/convert") } if !found2 { t.Error("Unable to find github.com/Masterminds/cookoo") } } func TestClone(t *testing.T) { cfg := &Config{} err := yaml.Unmarshal([]byte(yml), &cfg) if err != nil { t.Errorf("Unable to Unmarshal config yaml") } cfg2 := cfg.Clone() if cfg2.Name != "fake/testing" { t.Error("Config cloning failed") } if cfg2.License != "MIT" { t.Error("Config cloning failed to copy License") } cfg.Name = "foo" if cfg.Name == cfg2.Name { t.Error("Cloning Config name failed") } } func TestConfigFromYaml(t *testing.T) { c, err := ConfigFromYaml([]byte(yml)) if err != nil { t.Error("ConfigFromYaml failed to parse yaml") } if c.Name != "fake/testing" { t.Error("ConfigFromYaml failed to properly parse yaml") } } func TestHasDependency(t *testing.T) { c, err := ConfigFromYaml([]byte(yml)) if err != nil { t.Error("ConfigFromYaml failed to parse yaml for HasDependency") } if c.HasDependency("github.com/Masterminds/convert") != true { t.Error("HasDependency failing to pickup depenency") } if c.HasDependency("foo/bar/bar") != false { t.Error("HasDependency picking up dependency it shouldn't") } } func TestOwners(t *testing.T) { o := new(Owner) o.Name = "foo" o.Email = "foo@example.com" o.Home = "https://foo.example.com" o2 := o.Clone() if o2.Name != o.Name || o2.Email != o.Email || o2.Home != o.Home { t.Error("Unable to clone Owner") } o.Name = "Bar" if o.Name == o2.Name { t.Error("Owner clone is a pointer instead of a clone") } s := make(Owners, 0, 1) s = append(s, o) s2 := s.Clone() o3 := s2[0] o3.Name = "Qux" if o3.Name == o.Name { t.Error("Owners cloning isn't deep") } cfg := &Config{} err := yaml.Unmarshal([]byte(yml), &cfg) if err != nil { t.Errorf("Unable to Unmarshal config yaml") } if cfg.Owners[0].Name != "foo" || cfg.Owners[0].Email != "bar@example.com" || cfg.Owners[0].Home != "https://example.com" { t.Error("Unable to parse owners from yaml") } } ================================================ FILE: cfg/lock.go ================================================ package cfg import ( "crypto/sha256" "fmt" "io/ioutil" "sort" "strings" "time" "gopkg.in/yaml.v2" ) // Lockfile represents a glide.lock file. type Lockfile struct { Hash string `yaml:"hash"` Updated time.Time `yaml:"updated"` Imports Locks `yaml:"imports"` DevImports Locks `yaml:"testImports"` } // LockfileFromYaml returns an instance of Lockfile from YAML func LockfileFromYaml(yml []byte) (*Lockfile, error) { lock := &Lockfile{} err := yaml.Unmarshal([]byte(yml), &lock) return lock, err } // Marshal converts a Config instance to YAML func (lf *Lockfile) Marshal() ([]byte, error) { yml, err := yaml.Marshal(&lf) if err != nil { return []byte{}, err } 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 // the given file. If the file exists, it will be clobbered. func (lf *Lockfile) WriteFile(lockpath string) error { o, err := lf.Marshal() if err != nil { return err } return ioutil.WriteFile(lockpath, o, 0666) } // Clone returns a clone of Lockfile func (lf *Lockfile) Clone() *Lockfile { n := &Lockfile{} n.Hash = lf.Hash n.Updated = lf.Updated n.Imports = lf.Imports.Clone() n.DevImports = lf.DevImports.Clone() return n } // Fingerprint returns a hash of the contents minus the date. This allows for // two lockfiles to be compared irrespective of their updated times. func (lf *Lockfile) Fingerprint() ([32]byte, error) { c := lf.Clone() c.Updated = time.Time{} // Set the time to be the nil equivalent sort.Sort(c.Imports) sort.Sort(c.DevImports) yml, err := c.Marshal() if err != nil { return [32]byte{}, err } return sha256.Sum256(yml), nil } // ReadLockFile loads the contents of a glide.lock file. func ReadLockFile(lockpath string) (*Lockfile, error) { yml, err := ioutil.ReadFile(lockpath) if err != nil { return nil, err } lock, err := LockfileFromYaml(yml) if err != nil { return nil, err } return lock, nil } // Locks is a slice of locked dependencies. type Locks []*Lock // Clone returns a Clone of Locks. func (l Locks) Clone() Locks { n := make(Locks, 0, len(l)) for _, v := range l { n = append(n, v.Clone()) } return n } // Len returns the length of the Locks. This is needed for sorting with // the sort package. func (l Locks) Len() int { return len(l) } // Less is needed for the sort interface. It compares two locks based on // their name. func (l Locks) 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(l[i].Name) < strings.ToLower(l[j].Name) } // Swap is needed for the sort interface. It swaps the position of two // locks. func (l Locks) Swap(i, j int) { l[i], l[j] = l[j], l[i] } // Lock represents an individual locked dependency. type Lock struct { Name string `yaml:"name"` Version string `yaml:"version"` 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"` } // Clone creates a clone of a Lock. func (l *Lock) Clone() *Lock { return &Lock{ Name: l.Name, Version: l.Version, Repository: l.Repository, VcsType: l.VcsType, Subpackages: l.Subpackages, Arch: l.Arch, Os: l.Os, } } // LockFromDependency converts a Dependency to a Lock func LockFromDependency(dep *Dependency) *Lock { return &Lock{ Name: dep.Name, Version: dep.Pin, Repository: dep.Repository, VcsType: dep.VcsType, Subpackages: dep.Subpackages, Arch: dep.Arch, Os: dep.Os, } } // NewLockfile is used to create an instance of 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, 0), } for i := 0; i < len(ds); i++ { lf.Imports[i] = LockFromDependency(ds[i]) } sort.Sort(lf.Imports) var found bool for i := 0; i < len(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, nil } // LockfileFromMap takes a map of dependencies and generates a lock Lockfile instance. func LockfileFromMap(ds map[string]*Dependency, hash string) *Lockfile { lf := &Lockfile{ Hash: hash, Updated: time.Now(), Imports: make([]*Lock, len(ds)), } i := 0 for name, dep := range ds { lf.Imports[i] = LockFromDependency(dep) lf.Imports[i].Name = name i++ } sort.Sort(lf.Imports) return lf } ================================================ FILE: cfg/lock_test.go ================================================ package cfg import ( "sort" "strings" "testing" ) func TestSortLocks(t *testing.T) { c, err := ConfigFromYaml([]byte(yml)) if err != nil { t.Error("ConfigFromYaml failed to parse yaml for TestSortDependencies") } ls := make(Locks, len(c.Imports)) for i := 0; i < len(c.Imports); i++ { ls[i] = &Lock{ Name: c.Imports[i].Name, Version: c.Imports[i].Reference, } } if ls[2].Name != "github.com/Masterminds/structable" { t.Error("Initial dependencies are out of order prior to sort") } sort.Sort(ls) if ls[0].Name != "github.com/kylelemons/go-gypsy" || ls[1].Name != "github.com/Masterminds/convert" || ls[2].Name != "github.com/Masterminds/cookoo" || ls[3].Name != "github.com/Masterminds/structable" { 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) } } ================================================ FILE: dependency/resolver.go ================================================ package dependency import ( "container/list" "errors" "runtime" "sort" //"go/build" "os" "path/filepath" "strings" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/glide/util" ) // MissingPackageHandler handles the case where a package is missing during scanning. // // It returns true if the package can be passed to the resolver, false otherwise. // False may be returned even if error is nil. type MissingPackageHandler interface { // NotFound is called when the Resolver fails to find a package with the given name. // // NotFound returns true when the resolver should attempt to re-resole the // dependency (e.g. when NotFound has gone and fetched the missing package). // // When NotFound returns false, the Resolver does not try to do any additional // work on the missing package. // // NotFound only returns errors when it fails to perform its internal goals. // When it returns false with no error, this indicates that the handler did // its job, but the resolver should not do any additional work on the // package. NotFound(pkg string, addTest bool) (bool, error) // OnGopath is called when the Resolver finds a dependency, but it's only on GOPATH. // // OnGopath provides an opportunity to copy, move, warn, or ignore cases like this. // // OnGopath returns true when the resolver should attempt to re-resolve the // dependency (e.g. when the dependency is copied to a new location). // // When OnGopath returns false, the Resolver does not try to do any additional // work on the package. // // An error indicates that OnGopath cannot complete its intended operation. // Not all false results are errors. OnGopath(pkg string, addTest bool) (bool, error) // InVendor is called when the Resolver finds a dependency in the vendor/ directory. // // 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. // // When asked to handle a missing package, it will report the miss as a warning, // and then store the package in the Missing slice for later access. type DefaultMissingPackageHandler struct { Missing []string Gopath []string Prefix string } // NotFound prints a warning and then stores the package name in Missing. // // It never returns an error, and it always returns false. func (d *DefaultMissingPackageHandler) NotFound(pkg string, addTest bool) (bool, error) { msg.Warn("Package %s is not installed", pkg) d.Missing = append(d.Missing, pkg) return false, nil } // OnGopath is run when a package is missing from vendor/ but found in the GOPATH func (d *DefaultMissingPackageHandler) OnGopath(pkg string, addTest bool) (bool, error) { msg.Warn("Package %s is only on GOPATH.", pkg) d.Gopath = append(d.Gopath, pkg) return false, nil } // InVendor is run when a package is found in the vendor/ folder func (d *DefaultMissingPackageHandler) InVendor(pkg string, addTest bool) error { msg.Info("Package %s found in vendor/ folder", pkg) 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 // scanning its contents to be sure to pick up the right elements for that // version. type VersionHandler interface { // Process provides an opportunity to process the codebase for version setting. Process(pkg string) error // SetVersion sets the version for a package. An error is returned if there // was a problem setting the version. SetVersion(pkg string, testDep bool) error } // DefaultVersionHandler is the default handler for setting the version. // // The default handler leaves the current version and skips setting a version. // For a handler that alters the version see the handler included in the repo // package as part of the installer. type DefaultVersionHandler struct{} // Process a package to aide in version setting. func (d *DefaultVersionHandler) Process(pkg string) error { return nil } // SetVersion here sends a message when a package is found noting that it // did not set the version. func (d *DefaultVersionHandler) SetVersion(pkg string, testDep bool) error { msg.Warn("Version not set for package %s", pkg) return nil } // Resolver resolves a dependency tree. // // It operates in two modes: // - local resolution (ResolveLocal) determines the dependencies of the local project. // - vendor resolving (Resolve, ResolveAll) determines the dependencies of vendored // projects. // // Local resolution is for guessing initial dependencies. Vendor resolution is // for determining vendored dependencies. type Resolver struct { Handler MissingPackageHandler VersionHandler VersionHandler VendorDir string BuildContext *util.BuildCtxt Config *cfg.Config // ResolveAllFiles toggles deep scanning. // If this is true, resolve by scanning all files, not by walking the // import tree. ResolveAllFiles bool // ResolveTest sets if test dependencies should be resolved. ResolveTest bool // Items already in the queue. alreadyQ map[string]bool // Attempts to scan that had unrecoverable error. hadError map[string]bool basedir string seen map[string]bool // findCache caches hits from Find. This reduces the number of filesystem // touches that have to be done for dependency resolution. findCache map[string]*PkgInfo } // NewResolver returns a new Resolver initialized with the DefaultMissingPackageHandler. // // This will return an error if the given path does not meet the basic criteria // for a Go source project. For example, basedir must have a vendor subdirectory. // // The BuildContext uses the "go/build".Default to resolve dependencies. func NewResolver(basedir string) (*Resolver, error) { var err error basedir, err = filepath.Abs(basedir) if err != nil { return nil, err } basedir, err = checkForBasedirSymlink(basedir) if err != nil { return nil, err } vdir := filepath.Join(basedir, "vendor") buildContext, err := util.GetBuildContext() if err != nil { return nil, err } r := &Resolver{ Handler: &DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}}, VersionHandler: &DefaultVersionHandler{}, basedir: basedir, VendorDir: vdir, BuildContext: buildContext, seen: map[string]bool{}, alreadyQ: map[string]bool{}, hadError: map[string]bool{}, findCache: map[string]*PkgInfo{}, // The config instance here should really be replaced with a real one. Config: &cfg.Config{}, } // TODO: Make sure the build context is correctly set up. Especially in // regards to GOROOT, which is not always set. return r, nil } // Resolve takes a package name and returns all of the imported package names. // // If a package is not found, this calls the Fetcher. If the Fetcher returns // true, it will re-try traversing that package for dependencies. Otherwise it // will add that package to the deps array and continue on without trying it. // And if the Fetcher returns an error, this will stop resolution and return // the error. // // If basepath is set to $GOPATH, this will start from that package's root there. // If basepath is set to a project's vendor path, the scanning will begin from // there. func (r *Resolver) Resolve(pkg, basepath string) ([]string, error) { target := filepath.Join(basepath, filepath.FromSlash(pkg)) //msg.Debug("Scanning %s", target) l := list.New() l.PushBack(target) // In this mode, walk the entire tree. if r.ResolveAllFiles { return r.resolveList(l, false, false) } return r.resolveImports(l, false, false) } // dirHasPrefix tests whether the directory dir begins with prefix. func dirHasPrefix(dir, prefix string) bool { if runtime.GOOS != "windows" { return strings.HasPrefix(dir, prefix) } return len(dir) >= len(prefix) && strings.EqualFold(dir[:len(prefix)], prefix) } // ResolveLocal resolves dependencies for the current project. // // This begins with the project, builds up a list of external dependencies. // // If the deep flag is set to true, this will then resolve all of the dependencies // of the dependencies it has found. If not, it will return just the packages that // the base project relies upon. func (r *Resolver) ResolveLocal(deep bool) ([]string, []string, error) { // We build a list of local source to walk, then send this list // to resolveList. msg.Debug("Resolving local dependencies") l := list.New() tl := list.New() alreadySeen := map[string]bool{} talreadySeen := map[string]bool{} err := filepath.Walk(r.basedir, func(path string, fi os.FileInfo, err error) error { if err != nil && err != filepath.SkipDir { return err } pt := strings.TrimPrefix(path, r.basedir+string(os.PathSeparator)) pt = strings.TrimSuffix(pt, string(os.PathSeparator)) if r.Config.HasExclude(pt) { msg.Debug("Excluding %s", pt) return filepath.SkipDir } if !fi.IsDir() { return nil } if !srcDir(fi) { return filepath.SkipDir } // Scan for dependencies, and anything that's not part of the local // package gets added to the scan list. var imps []string var testImps []string p, err := r.BuildContext.ImportDir(path, 0) if err != nil { if strings.HasPrefix(err.Error(), "no buildable Go source") { return nil } else if 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. imps, testImps, err = IterativeScan(path) if err != nil { return err } } else { return err } } else { imps = p.Imports testImps = dedupeStrings(p.TestImports, p.XTestImports) } // We are only looking for dependencies in vendor. No root, cgo, etc. for _, imp := range imps { if r.Config.HasIgnore(imp) { continue } if alreadySeen[imp] { continue } alreadySeen[imp] = true info := r.FindPkg(imp) switch info.Loc { case LocUnknown, LocVendor: l.PushBack(filepath.Join(r.VendorDir, filepath.FromSlash(imp))) // Do we need a path on this? case LocGopath: if !dirHasPrefix(info.Path, r.basedir) { // FIXME: This is a package outside of the project we're // scanning. It should really be on vendor. But we don't // want it to reference GOPATH. We want it to be detected // and moved. l.PushBack(filepath.Join(r.VendorDir, filepath.FromSlash(imp))) } case LocRelative: if strings.HasPrefix(imp, "./"+gpath.VendorDir) { msg.Warn("Go package resolving will resolve %s without the ./%s/ prefix", imp, gpath.VendorDir) } } } if r.ResolveTest { for _, imp := range testImps { if talreadySeen[imp] { continue } talreadySeen[imp] = true info := r.FindPkg(imp) switch info.Loc { case LocUnknown, LocVendor: tl.PushBack(filepath.Join(r.VendorDir, filepath.FromSlash(imp))) // Do we need a path on this? case LocGopath: if !dirHasPrefix(info.Path, r.basedir) { // FIXME: This is a package outside of the project we're // scanning. It should really be on vendor. But we don't // want it to reference GOPATH. We want it to be detected // and moved. tl.PushBack(filepath.Join(r.VendorDir, filepath.FromSlash(imp))) } case LocRelative: if strings.HasPrefix(imp, "./"+gpath.VendorDir) { msg.Warn("Go package resolving will resolve %s without the ./%s/ prefix", imp, gpath.VendorDir) } } } } return nil }) if err != nil { msg.Err("Failed to build an initial list of packages to scan: %s", err) return []string{}, []string{}, err } if deep { if r.ResolveAllFiles { re, err := r.resolveList(l, false, false) if err != nil { return []string{}, []string{}, err } tre, err := r.resolveList(l, false, true) return re, tre, err } re, err := r.resolveImports(l, false, false) if err != nil { return []string{}, []string{}, err } tre, err := r.resolveImports(tl, true, true) return re, tre, err } // If we're not doing a deep scan, we just convert the list into an // array and return. res := make([]string, 0, l.Len()) for e := l.Front(); e != nil; e = e.Next() { res = append(res, e.Value.(string)) } tres := make([]string, 0, l.Len()) if r.ResolveTest { for e := tl.Front(); e != nil; e = e.Next() { tres = append(tres, e.Value.(string)) } } return res, tres, nil } // ResolveAll takes a list of packages and returns an inclusive list of all // vendored dependencies. // // While this will scan all of the source code it can find, it will only return // packages that were either explicitly passed in as deps, or were explicitly // imported by the code. // // Packages that are either CGO or on GOROOT are ignored. Packages that are // on GOPATH, but not vendored currently generate a warning. // // If one of the passed in packages does not exist in the vendor directory, // an error is returned. func (r *Resolver) ResolveAll(deps []*cfg.Dependency, addTest bool) ([]string, error) { queue := sliceToQueue(deps, r.VendorDir) if r.ResolveAllFiles { return r.resolveList(queue, false, addTest) } return r.resolveImports(queue, false, addTest) } // Stripv strips the vendor/ prefix from vendored packages. func (r *Resolver) Stripv(str string) string { return strings.TrimPrefix(str, r.VendorDir+string(os.PathSeparator)) } // vpath adds an absolute vendor path. func (r *Resolver) vpath(str string) string { return filepath.Join(r.basedir, "vendor", str) } // resolveImports takes a list of existing packages and resolves their imports. // // It returns a list of all of the packages that it can determine are required // for the given code to function. // // The expectation is that each item in the queue is an absolute path to a // vendored package. This attempts to read that package, and then find // its referenced packages. Those packages are then added to the list // to be scanned next. // // The resolver's handler is used in the cases where a package cannot be // located. // // testDeps specifies if the test dependencies should be resolved and addTest // specifies if the dependencies should be added to the Config.DevImports. This // is important because we may resolve normal dependencies of test deps and add // them to the DevImports list. func (r *Resolver) resolveImports(queue *list.List, testDeps, addTest bool) ([]string, error) { msg.Debug("Resolving import path") // When test deps passed in but not resolving return empty. if (testDeps || addTest) && !r.ResolveTest { return []string{}, nil } alreadySeen := make(map[string]bool, queue.Len()) for e := queue.Front(); e != nil; e = e.Next() { vdep := e.Value.(string) dep := r.Stripv(vdep) // Check if marked in the Q and then explicitly mark it. We want to know // if it had previously been marked and ensure it for the future. if alreadySeen[dep] { continue } alreadySeen[dep] = true _, foundQ := r.alreadyQ[dep] r.alreadyQ[dep] = true // If we've already encountered an error processing this dependency // skip it. _, foundErr := r.hadError[dep] if foundErr { continue } // Skip ignored packages if r.Config.HasIgnore(dep) { msg.Debug("Ignoring: %s", dep) continue } r.VersionHandler.Process(dep) // Here, we want to import the package and see what imports it has. msg.Debug("Trying to open %s (%s)", dep, r.Handler.PkgPath(dep)) var imps []string 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 // or main but +build ignore as a build tag. In that case we // try to brute force the packages with a slower scan. msg.Debug("Using Iterative Scanning for %s", dep) if testDeps { _, imps, err = IterativeScan(r.Handler.PkgPath(dep)) } else { imps, _, err = IterativeScan(r.Handler.PkgPath(dep)) } if err != nil { msg.Err("Iterative scanning error %s: %s", dep, err) continue } } else if err != nil { 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 osDirNotFound(err, r.Handler.PkgPath(dep)) && !foundErr && !foundQ { // If the location doesn't exist, there hasn't already been an // error, it's not already been in the Q then try to fetch it. // When there's an error or it's already in the Q (it should be // fetched if it's marked in r.alreadyQ) we skip to make sure // not to get stuck in a recursion. // If the location doesn't exist try to fetch it. if ok, err2 := r.Handler.NotFound(dep, addTest); ok { r.alreadyQ[dep] = true alreadySeen[dep] = false // By adding to the queue it will get reprocessed now that // it exists. queue.PushBack(r.vpath(dep)) r.VersionHandler.SetVersion(dep, addTest) } else if err2 != nil { r.hadError[dep] = true msg.Err("Error looking for %s: %s", dep, err2) } else { r.hadError[dep] = true // TODO (mpb): Should we toss this into a Handler to // 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) } continue } else { if testDeps { imps = dedupeStrings(pkg.TestImports, pkg.XTestImports) } else { imps = pkg.Imports } } // Range over all of the identified imports and see which ones we // can locate. for _, imp := range imps { if r.Config.HasIgnore(imp) { msg.Debug("Ignoring: %s", imp) continue } pi := r.FindPkg(imp) if pi.Loc != LocCgo && pi.Loc != LocGoroot && pi.Loc != LocAppengine { msg.Debug("Package %s imports %s", dep, imp) } switch pi.Loc { case LocVendor: msg.Debug("In vendor: %s", imp) if _, ok := r.alreadyQ[imp]; !ok { msg.Debug("Marking %s to be scanned.", imp) r.alreadyQ[imp] = true queue.PushBack(r.vpath(imp)) if err := r.Handler.InVendor(imp, addTest); err == nil { r.VersionHandler.SetVersion(imp, addTest) } else { msg.Warn("Error updating %s: %s", imp, err) } } case LocUnknown: msg.Debug("Missing %s. Trying to resolve.", imp) if ok, err := r.Handler.NotFound(imp, addTest); ok { r.alreadyQ[imp] = true queue.PushBack(r.vpath(imp)) r.VersionHandler.SetVersion(imp, addTest) } else if err != nil { r.hadError[imp] = true msg.Err("Error looking for %s: %s", imp, err) } else { r.hadError[imp] = true msg.Err("Not found: %s (2)", imp) } case LocGopath: msg.Debug("Found on GOPATH, not vendor: %s", imp) if _, ok := r.alreadyQ[imp]; !ok { // Only scan it if it gets moved into vendor/ if ok, _ := r.Handler.OnGopath(imp, addTest); ok { r.alreadyQ[imp] = true queue.PushBack(r.vpath(imp)) r.VersionHandler.SetVersion(imp, addTest) } } } } } 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()) // In addition to generating a list for e := queue.Front(); e != nil; e = e.Next() { t := r.Stripv(e.Value.(string)) root, sp := util.NormalizeName(t) if root == r.Config.Name { continue } // Skip ignored packages if r.Config.HasIgnore(e.Value.(string)) { msg.Debug("Ignoring: %s", e.Value.(string)) continue } // TODO(mattfarina): Need to eventually support devImport existing := r.Config.Imports.Get(root) if existing == nil && addTest { existing = r.Config.DevImports.Get(root) } if existing != nil { if sp != "" && !existing.HasSubpackage(sp) { existing.Subpackages = append(existing.Subpackages, sp) } } else { newDep := &cfg.Dependency{ Name: root, } if sp != "" { newDep.Subpackages = []string{sp} } if addTest { r.Config.DevImports = append(r.Config.DevImports, newDep) } else { r.Config.Imports = append(r.Config.Imports, newDep) } } res = append(res, t) } return res, nil } // resolveList takes a list and resolves it. // // This walks the entire file tree for the given dependencies, not just the // parts that are imported directly. Using this will discover dependencies // regardless of OS, and arch. func (r *Resolver) resolveList(queue *list.List, testDeps, addTest bool) ([]string, error) { // When test deps passed in but not resolving return empty. if testDeps && !r.ResolveTest { return []string{}, nil } var failedDep string var failedDepPath string var pkgPath string for e := queue.Front(); e != nil; e = e.Next() { dep := e.Value.(string) t := strings.TrimPrefix(dep, r.VendorDir+string(os.PathSeparator)) if r.Config.HasIgnore(t) { msg.Debug("Ignoring: %s", t) continue } r.VersionHandler.Process(t) //msg.Warn("#### %s ####", dep) //msg.Info("Seen Count: %d", len(r.seen)) // Catch the outtermost dependency. pkgPath = r.Handler.PkgPath(t) failedDep = t failedDepPath = pkgPath err := filepath.Walk(pkgPath, func(path string, fi os.FileInfo, err error) error { if err != nil && err != filepath.SkipDir { return err } // Skip files. if !fi.IsDir() { return nil } // Skip dirs that are not source. if !srcDir(fi) { //msg.Debug("Skip resource %s", fi.Name()) return filepath.SkipDir } // Anything that comes through here has already been through // the queue. r.alreadyQ[path] = true e := r.queueUnseen(path, queue, testDeps, addTest) if e != nil { failedDepPath = path //msg.Err("Failed to fetch dependency %s: %s", path, err) } return e }) if err != nil && err != filepath.SkipDir { msg.Err("Dependency %s (%s) failed to resolve: %s.", failedDep, failedDepPath, err) return []string{}, err } } res := make([]string, 0, queue.Len()) // In addition to generating a list for e := queue.Front(); e != nil; e = e.Next() { t := strings.TrimPrefix(e.Value.(string), r.VendorDir+string(os.PathSeparator)) root, sp := util.NormalizeName(t) if root == r.Config.Name { continue } existing := r.Config.Imports.Get(root) if existing == nil && addTest { existing = r.Config.DevImports.Get(root) } if existing != nil { if sp != "" && !existing.HasSubpackage(sp) { existing.Subpackages = append(existing.Subpackages, sp) } } else { newDep := &cfg.Dependency{ Name: root, } if sp != "" { newDep.Subpackages = []string{sp} } if addTest { r.Config.DevImports = append(r.Config.DevImports, newDep) } else { r.Config.Imports = append(r.Config.Imports, newDep) } } res = append(res, e.Value.(string)) } return res, nil } // queueUnseenImports scans a package's imports and adds any new ones to the // processing queue. func (r *Resolver) queueUnseen(pkg string, queue *list.List, testDeps, addTest bool) error { // A pkg is marked "seen" as soon as we have inspected it the first time. // Seen means that we have added all of its imports to the list. // Already queued indicates that we've either already put it into the queue // or intentionally not put it in the queue for fatal reasons (e.g. no // buildable source). deps, err := r.imports(pkg, testDeps, addTest) if err != nil && !strings.HasPrefix(err.Error(), "no buildable Go source") { msg.Err("Could not find %s: %s", pkg, err) return err // NOTE: If we uncomment this, we get lots of "no buildable Go source" errors, // which don't ever seem to be helpful. They don't actually indicate an error // condition, and it's perfectly okay to run into that condition. //} else if err != nil { // msg.Warn(err.Error()) } for _, d := range deps { if _, ok := r.alreadyQ[d]; !ok { r.alreadyQ[d] = true queue.PushBack(d) } } return nil } // imports gets all of the imports for a given package. // // If the package is in GOROOT, this will return an empty list (but not // an error). // If it cannot resolve the pkg, it will return an error. func (r *Resolver) imports(pkg string, testDeps, addTest bool) ([]string, error) { if r.Config.HasIgnore(pkg) { msg.Debug("Ignoring %s", pkg) return []string{}, nil } // If this pkg is marked seen, we don't scan it again. if _, ok := r.seen[pkg]; ok { msg.Debug("Already saw %s", pkg) return []string{}, nil } // 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) 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(r.Handler.PkgPath(pkg)) } else { imps, _, err = IterativeScan(r.Handler.PkgPath(pkg)) } if err != nil { return []string{}, err } } else if err != nil { return []string{}, err } else { if testDeps { imps = dedupeStrings(p.TestImports, p.XTestImports) } else { imps = p.Imports } } // It is okay to scan a package more than once. In some cases, this is // desirable because the package can change between scans (e.g. as a result // of a failed scan resolving the situation). msg.Debug("=> Scanning %s (%s)", p.ImportPath, pkg) r.seen[pkg] = true // Optimization: If it's in GOROOT, it has no imports worth scanning. if p.Goroot { return []string{}, nil } // We are only looking for dependencies in vendor. No root, cgo, etc. buf := []string{} for _, imp := range imps { if r.Config.HasIgnore(imp) { msg.Debug("Ignoring %s", imp) continue } info := r.FindPkg(imp) switch info.Loc { case LocUnknown: // Do we resolve here? found, err := r.Handler.NotFound(imp, addTest) if err != nil { msg.Err("Failed to fetch %s: %s", imp, err) } if found { buf = append(buf, filepath.Join(r.VendorDir, filepath.FromSlash(imp))) r.VersionHandler.SetVersion(imp, addTest) continue } r.seen[info.Path] = true case LocVendor: //msg.Debug("Vendored: %s", imp) buf = append(buf, info.Path) if err := r.Handler.InVendor(imp, addTest); err == nil { r.VersionHandler.SetVersion(imp, addTest) } else { msg.Warn("Error updating %s: %s", imp, err) } case LocGopath: found, err := r.Handler.OnGopath(imp, addTest) if err != nil { msg.Err("Failed to fetch %s: %s", imp, err) } // If the Handler marks this as found, we drop it into the buffer // for subsequent processing. Otherwise, we assume that we're // in a less-than-perfect, but functional, situation. if found { buf = append(buf, filepath.Join(r.VendorDir, filepath.FromSlash(imp))) r.VersionHandler.SetVersion(imp, addTest) continue } msg.Warn("Package %s is on GOPATH, but not vendored. Ignoring.", imp) r.seen[info.Path] = true default: // Local packages are an odd case. CGO cannot be scanned. msg.Debug("===> Skipping %s", imp) } } return buf, nil } // sliceToQueue is a special-purpose function for unwrapping a slice of // dependencies into a queue of fully qualified paths. func sliceToQueue(deps []*cfg.Dependency, basepath string) *list.List { l := list.New() for _, e := range deps { if len(e.Subpackages) > 0 { for _, v := range e.Subpackages { ip := e.Name if v != "." && v != "" { ip = ip + "/" + v } msg.Debug("Adding local Import %s to queue", ip) l.PushBack(filepath.Join(basepath, filepath.FromSlash(ip))) } } else { msg.Debug("Adding local Import %s to queue", e.Name) l.PushBack(filepath.Join(basepath, filepath.FromSlash(e.Name))) } } return l } // PkgLoc describes the location of the package. type PkgLoc uint8 const ( // LocUnknown indicates the package location is unknown (probably not present) LocUnknown PkgLoc = iota // LocLocal inidcates that the package is in a local dir, not GOPATH or GOROOT. LocLocal // LocVendor indicates that the package is in a vendor/ dir LocVendor // LocGopath inidcates that the package is in GOPATH LocGopath // LocGoroot indicates that the package is in GOROOT LocGoroot // LocCgo indicates that the package is a a CGO package LocCgo // LocAppengine indicates the package is part of the appengine SDK. It's a // special build mode. https://blog.golang.org/the-app-engine-sdk-and-workspaces-gopath // Why does a Google product get a special case build mode with a local // package? LocAppengine // LocRelative indicates the package is a relative directory LocRelative ) // PkgInfo represents metadata about a package found by the resolver. type PkgInfo struct { Name, Path string Vendored bool Loc PkgLoc } // PackagesAddedToStdlib is the list of packages added to the go standard lib // at various points. var PackagesAddedToStdlib = map[string]struct{}{ // context and net/http/httptrace are packages being added to // the Go 1.7 standard library. Some packages, such as golang.org/x/net // are importing it with build flags in files for go1.7. "context": struct{}{}, "net/http/httptrace": struct{}{}, // math.bits are packages being added to the Go 1.9 standard library. // Some packages, such as github.com/RoaringBitmap/roaring are importing // it with build flags in files for go1.9. "math/bits": struct{}{}, // crypto/ed25519 is a package being added to the Go 1.13 standard library. // It is importing itself with build flags in files for go1.13. "crypto/ed25519": struct{}{}, } // FindPkg takes a package name and attempts to find it on the filesystem // // The resulting PkgInfo will indicate where it was found. func (r *Resolver) FindPkg(name string) *PkgInfo { // We cachae results for FindPkg to reduce the number of filesystem ops // that we have to do. This is a little risky because certain directories, // like GOPATH, can be modified while we're running an operation, and // render the cache inaccurate. // // Unfound items (LocUnknown) are never cached because we assume that as // part of the response, the Resolver may fetch that dependency. if i, ok := r.findCache[name]; ok { //msg.Info("Cache hit on %s", name) return i } // 502 individual packages scanned. // No cache: // glide -y etcd.yaml list 0.27s user 0.19s system 85% cpu 0.534 total // With cache: // glide -y etcd.yaml list 0.22s user 0.15s system 85% cpu 0.438 total var p string info := &PkgInfo{ Name: name, } if strings.HasPrefix(name, "./") || strings.HasPrefix(name, "../") { info.Loc = LocRelative r.findCache[name] = info return info } // Check _only_ if this dep is in the current vendor directory. p = filepath.Join(r.VendorDir, filepath.FromSlash(name)) if pkgExists(p) { info.Path = p info.Loc = LocVendor info.Vendored = true r.findCache[name] = info return info } // TODO: Do we need this if we always flatten? // Recurse backward to scan other vendor/ directories //for wd := cwd; wd != "/"; wd = filepath.Dir(wd) { //p = filepath.Join(wd, "vendor", filepath.FromSlash(name)) //if fi, err = os.Stat(p); err == nil && (fi.IsDir() || isLink(fi)) { //info.Path = p //info.PType = ptypeVendor //info.Vendored = true //return info //} //} // Check $GOPATH for _, rr := range filepath.SplitList(r.BuildContext.GOPATH) { p = filepath.Join(rr, "src", filepath.FromSlash(name)) if pkgExists(p) { info.Path = p info.Loc = LocGopath r.findCache[name] = info return info } } // Check $GOROOT for _, rr := range filepath.SplitList(r.BuildContext.GOROOT) { p = filepath.Join(rr, "src", filepath.FromSlash(name)) if pkgExists(p) { info.Path = p info.Loc = LocGoroot r.findCache[name] = info return info } } // If this is "C", we're dealing with cgo if name == "C" { info.Loc = LocCgo r.findCache[name] = info } else if name == "appengine" || name == "appengine_internal" || strings.HasPrefix(name, "appengine/") || strings.HasPrefix(name, "appengine_internal/") { // Appengine is a special case when it comes to Go builds. It is a local // looking package only available within appengine. It's a special case // where Google products are playing with each other. // https://blog.golang.org/the-app-engine-sdk-and-workspaces-gopath info.Loc = LocAppengine r.findCache[name] = info } else if _, ok := PackagesAddedToStdlib[name]; ok { // Various packages are being added to the Go standard library, and being imported // with build flags. Need to detect this and handle it. info.Loc = LocGoroot r.findCache[name] = info } return info } func pkgExists(path string) bool { fi, err := os.Stat(path) return err == nil && (fi.IsDir() || isLink(fi)) } // isLink returns true if the given FileInfo is a symbolic link. func isLink(fi os.FileInfo) bool { return fi.Mode()&os.ModeSymlink == os.ModeSymlink } // IsSrcDir returns true if this is a directory that could have source code, // false otherwise. // // Directories with _ or . prefixes are skipped, as are testdata and vendor. func IsSrcDir(fi os.FileInfo) bool { return srcDir(fi) } func srcDir(fi os.FileInfo) bool { if !fi.IsDir() { return false } // Ignore _foo and .foo if strings.HasPrefix(fi.Name(), "_") || strings.HasPrefix(fi.Name(), ".") { return false } // Ignore testdata. For now, ignore vendor. if fi.Name() == "testdata" || fi.Name() == "vendor" { return false } return true } // checkForBasedirSymlink checks to see if the given basedir is actually a // symlink. In the case that it is a symlink, the symlink is read and returned. // If the basedir is not a symlink, the provided basedir argument is simply // returned back to the caller. func checkForBasedirSymlink(basedir string) (string, error) { fi, err := os.Lstat(basedir) if err != nil { return "", err } if fi.Mode()&os.ModeSymlink != 0 { return os.Readlink(basedir) } 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 } // In Go 1.9 go/build.ImportDir changed so that a missing dir // no longer responses with os.IsNotExist. Instead the error changed // one in the form of fmt.Errorf("cannot find package %q in:\n\t%s", path, p.Dir) // which is similar to other go/build.ImportDir errors. This function // attempts to detect when ImportDir thinks something is not found func osDirNotFound(err error, p string) bool { if os.IsNotExist(err) { return true } // Since there are multiple errors that start like this we need to make // sure the directory is not present if strings.HasPrefix(err.Error(), "cannot find package ") { _, nferr := os.Stat(p) if os.IsNotExist(nferr) { return true } } return false } ================================================ FILE: dependency/resolver_test.go ================================================ package dependency import ( "os" "path/filepath" "strings" "testing" "github.com/Masterminds/glide/cfg" ) func TestResolveLocalShallow(t *testing.T) { r, err := NewResolver("../") if err != nil { t.Fatal(err) } l, _, err := r.ResolveLocal(false) if err != nil { t.Fatalf("Failed to resolve: %s", err) } expect := []string{ filepath.FromSlash("github.com/Masterminds/semver"), filepath.FromSlash("github.com/Masterminds/vcs"), filepath.FromSlash("gopkg.in/yaml.v2"), filepath.FromSlash("github.com/codegangsta/cli"), } for _, p := range expect { found := false for _, li := range l { if strings.HasSuffix(li, p) { found = true break } } if !found { t.Errorf("Could not find %s in resolved list.", p) } } } func TestResolveLocalDeep(t *testing.T) { r, err := NewResolver("../") 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 { t.Fatalf("Failed to resolve: %s", err) } if len(l) < 4 { t.Errorf("Expected at least 4 deps, got %d", len(l)) } } func TestResolve(t *testing.T) { r, err := NewResolver("../") 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) if err != nil { t.Fatalf("Failed to resolve: %s", err) } if len(l) != 1 { t.Errorf("Expected 1 dep, got %d: %s", len(l), l[0]) } if !strings.HasSuffix(filepath.FromSlash("github.com/codegangsta/cli"), l[0]) { t.Errorf("Unexpected package name: %s", l[0]) } } func TestResolveAll(t *testing.T) { // These are build dependencies of Glide, so we know they are here. deps := []*cfg.Dependency{ {Name: "github.com/codegangsta/cli"}, {Name: "github.com/Masterminds/semver"}, {Name: "github.com/Masterminds/vcs"}, {Name: "gopkg.in/yaml.v2"}, } r, err := NewResolver("../") 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) } if len(l) < len(deps) { t.Errorf("Expected at least %d deps, got %d", len(deps), len(l)) } } ================================================ FILE: dependency/scan.go ================================================ package dependency import ( "bytes" "io" "os" "path/filepath" "strings" "text/scanner" "github.com/Masterminds/glide/msg" "github.com/Masterminds/glide/util" ) var osList []string var archList []string func init() { // The supported systems are listed in // https://github.com/golang/go/blob/master/src/go/build/syslist.go // The lists are not exported so we need to duplicate them here. osListString := "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows" osList = strings.Split(osListString, " ") archListString := "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc s390 s390x sparc sparc64" archList = strings.Split(archListString, " ") } // IterativeScan attempts to obtain a list of imported dependencies from a // package. This scanning is different from ImportDir as part of the go/build // package. It looks over different permutations of the supported OS/Arch to // try and find all imports. This is different from setting UseAllFiles to // true on the build Context. It scopes down to just the supported OS/Arch. // // Note, there are cases where multiple packages are in the same directory. This // usually happens with an example that has a main package and a +build tag // of ignore. This is a bit of a hack. It causes UseAllFiles to have errors. func IterativeScan(path string) ([]string, []string, error) { // TODO(mattfarina): Add support for release tags. tgs, _ := readBuildTags(path) // Handle the case of scanning with no tags tgs = append(tgs, "") var pkgs []string var testPkgs []string for _, tt := range tgs { // split the tag combination to look at permutations. ts := strings.Split(tt, ",") var ttgs []string var arch string var ops string for _, ttt := range ts { dirty := false if strings.HasPrefix(ttt, "!") { dirty = true ttt = strings.TrimPrefix(ttt, "!") } if isSupportedOs(ttt) { if dirty { ops = getOsValue(ttt) } else { ops = ttt } } else if isSupportedArch(ttt) { if dirty { arch = getArchValue(ttt) } else { arch = ttt } } else { if !dirty { ttgs = append(ttgs, ttt) } } } // Handle the case where there are no tags but we need to iterate // on something. if len(ttgs) == 0 { ttgs = append(ttgs, "") } b, err := util.GetBuildContext() if err != nil { return []string{}, []string{}, err } // Make sure use all files is off b.UseAllFiles = false // Set the OS and Arch for this pass b.GOARCH = arch b.GOOS = ops b.BuildTags = ttgs msg.Debug("Scanning with Arch(%s), OS(%s), and Build Tags(%v)", arch, ops, ttgs) pk, err := b.ImportDir(path, 0) // If there are no buildable souce with this permutation we skip it. if err != nil && strings.HasPrefix(err.Error(), "no buildable Go source files in") { continue } else if err != nil && strings.HasPrefix(err.Error(), "found packages ") { // A permutation may cause multiple packages to appear. For example, // an example file with an ignore build tag. If this happens we // ignore it. // TODO(mattfarina): Find a better way. msg.Debug("Found multiple packages while scanning %s: %s", path, err) continue } else if err != nil { msg.Debug("Problem parsing package at %s for %s %s", path, ops, arch) return []string{}, []string{}, err } for _, dep := range pk.Imports { found := false for _, p := range pkgs { if p == dep { found = true } } if !found { pkgs = append(pkgs, dep) } } for _, dep := range pk.TestImports { found := false for _, p := range pkgs { if p == dep { found = true } } if !found { 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 } func readBuildTags(p string) ([]string, error) { _, err := os.Stat(p) if err != nil { return []string{}, err } d, err := os.Open(p) if err != nil { return []string{}, err } objects, err := d.Readdir(-1) if err != nil { return []string{}, err } var tags []string for _, obj := range objects { // only process Go files if strings.HasSuffix(obj.Name(), ".go") { fp := filepath.Join(p, obj.Name()) co, err := readGoContents(fp) if err != nil { return []string{}, err } // Only look at places where we had a code comment. if len(co) > 0 { t := findTags(co) for _, tg := range t { found := false for _, tt := range tags { if tt == tg { found = true } } if !found { tags = append(tags, tg) } } } } } return tags, nil } // Read contents of a Go file up to the package declaration. This can be used // to find the the build tags. func readGoContents(fp string) ([]byte, error) { f, err := os.Open(fp) defer f.Close() if err != nil { return []byte{}, err } var s scanner.Scanner s.Init(f) var tok rune var pos scanner.Position for tok != scanner.EOF { tok = s.Scan() // Getting the token text will skip comments by default. tt := s.TokenText() // build tags will not be after the package declaration. if tt == "package" { pos = s.Position break } } buf := bytes.NewBufferString("") f.Seek(0, 0) _, err = io.CopyN(buf, f, int64(pos.Offset)) if err != nil { return []byte{}, err } return buf.Bytes(), nil } // From a byte slice of a Go file find the tags. func findTags(co []byte) []string { p := co var tgs []string for len(p) > 0 { line := p if i := bytes.IndexByte(line, '\n'); i >= 0 { line, p = line[:i], p[i+1:] } else { p = p[len(p):] } line = bytes.TrimSpace(line) // Only look at comment lines that are well formed in the Go style if bytes.HasPrefix(line, []byte("//")) { line = bytes.TrimSpace(line[len([]byte("//")):]) if len(line) > 0 && line[0] == '+' { f := strings.Fields(string(line)) // We've found a +build tag line. if f[0] == "+build" { for _, tg := range f[1:] { tgs = append(tgs, tg) } } } } } return tgs } // Get an OS value that's not the one passed in. func getOsValue(n string) string { for _, o := range osList { if o != n { return o } } return n } func isSupportedOs(n string) bool { for _, o := range osList { if o == n { return true } } return false } // Get an Arch value that's not the one passed in. func getArchValue(n string) string { for _, o := range archList { if o != n { return o } } return n } func isSupportedArch(n string) bool { for _, o := range archList { if o == n { return true } } return false } ================================================ FILE: docs/commands.md ================================================ # Commands The following are the Glide commands, most of which are to help you manage your workspace. ## glide create (aliased to init) 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 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] You can download one or more packages to your `vendor` directory and have it added to your `glide.yaml` file with `glide get`. $ glide get github.com/Masterminds/cookoo When `glide get` is used it will introspect the listed package to resolve its dependencies including using Godep, GPM, Gom, and GB config files. The `glide get` command can have a [version or range](versions.md) passed in with the package name. For example, $ glide get github.com/Masterminds/cookoo#^1.2.3 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 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. 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`). 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 `glide install`. $ glide install This will read the `glide.lock` file, warning you if it's not tied to the `glide.yaml` file, and install the commit id specific versions there. When the `glide.lock` file doesn't tie to the `glide.yaml` file, such as there being a change, it will provide an warning. Running `glide up` will recreate the `glide.lock` file when updating the dependency tree. 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`. $ go test $(glide novendor) This will run `go test` over all directories of your project except the `vendor` directory. ## glide name When you're scripting with Glide there are occasions where you need to know the name of the package you're working on. `glide name` returns the name of the package listed in the `glide.yaml` file. ## glide list 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 help Print the glide help. $ glide help ## glide --version Print the version and exit. $ 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 manage 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 or 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 ================================================ FILE: docs/example-glide.yaml ================================================ # The name of this package. package: github.com/Masterminds/glide # External dependencies. import: # Minimal definition # This will use "go get [-u]" to fetch and update the package, and it will # attempt to keep the release at the tip of master. It does this by looking # for telltale signs that this is a git, bzr, or hg repo, and then acting # accordingly. - package: github.com/kylelemons/go-gypsy # Full definition # This will check out the given Git repo, set the version to master, # use "git" (not "go get") to manage it, and alias the package to the # import path github.com/Masterminds/cookoo - package: github.com/Masterminds/cookoo vcs: git version: master repo: git@github.com:Masterminds/cookoo.git # Here's an example with a commit hash for a version. Since repo is not # specified, this will use git to to try to clone # 'http://github.com/aokoli/goutils' and then set the revision to the given # hash. - package: github.com/aokoli/goutils vcs: git version: 9c37978a95bd5c709a15883b6242714ea6709e64 # MASKING: This takes my fork of goamz (technosophos/goamz) and clones it # as if it were the crowdmob/goamz package. This is incredibly useful for # masking packages and/or working with forks or clones. # # Note that absolutely no namespace munging happens on the code. If you want # that, you'll have to do it on your own. The intent of this masking was to # make it so you don't have to vendor imports. - package: github.com/crowdmob/goamz vcs: git repo: git@github.com:technosophos/goamz.git - package: bzr.example.com/foo/bar/trunk vcs: bzr repo: bzr://bzr.example.com/foo/bar/trunk # The version can be a branch, tag, commit id, or a semantic version # constraint parsable by https://github.com/Masterminds/semver version: 1.0.0 - package: hg.example.com/foo/bar vcs: hg repo: http://hg.example.com/foo/bar version: ae081cd1d6cc # For SVN, the only valid version is a commit number. Tags and branches go in # the repo URL. - package: svn.example.com/foo/bar/trunk vcs: svn repo: http://svn.example.com/foo/bar/trunk # If a package is dependent on OS, you can tell Glide to only # fetch for certain OS or architectures. # # os can be any valid GOOS. # arch can be any valid GOARCH. - package: github.com/unixy/package os: - linux - darwin arch: - amd64 ================================================ FILE: docs/faq.md ================================================ # Frequently Asked Questions (F.A.Q.) ## Q: Why does Glide have the concept of sub-packages when Go doesn't? In Go every directory is a package. This works well when you have one repo containing all of your packages. When you have different packages in different VCS locations things become a bit more complicated. A project containing a collection of packages should be handled with the same information including the version. By grouping packages this way we are able to manage the related information. ## Q: bzr (or hg) is not working the way I expected. Why? These are works in progress, and may need some additional tuning. Please take a look at the [vcs package](https://github.com/masterminds/vcs). If you see a better way to handle it please let us know. ## Q: Should I check `vendor/` into version control? That's up to you. It's a personal or organizational decision. Glide will help you install the outside dependencies on demand or help you manage the dependencies as they are checked into your version control system. By default, commands such as `glide update` and `glide install` install on-demand. To manage a vendor folder that's checked into version control use the flags: * `--update-vendored` (aliased to `-u`) to update the vendored dependencies. * `--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. ## Q: How do I import settings from GPM, Godep, Gom, or GB? There are two parts to importing. 1. If a package you import has configuration for GPM, Godep, Gom, or GB Glide will recursively install the dependencies automatically. 2. If you would like to import configuration from GPM, Godep, Gom, or GB to Glide see the `glide import` command. For example, you can run `glide import godep` for Glide to detect the projects Godep configuration and generate a `glide.yaml` file for you. Each of these will merge your existing `glide.yaml` file with the dependencies it finds for those managers, and then emit the file as output. **It will not overwrite your glide.yaml file.** You can write it to file like this: $ glide import godep -f glide.yaml ## Q: Can Glide fetch a package based on OS or Arch? Yes. Using the `os` and `arch` fields on a `package`, you can specify which OSes and architectures the package should be fetched for. For example, the following package will only be fetched for 64-bit Darwin/OSX systems: - package: some/package os: - darwin arch: - amd64 The package will not be fetched for other architectures or OSes. ## Q: How did Glide get its name? Aside from being catchy, "glide" is a contraction of "Go Elide". The idea is to compress the tasks that normally take us lots of time into a just a few seconds. ================================================ FILE: docs/getting-started.md ================================================ # Getting Started With Glide This is a quick start guide to using Glide once you have it installed. ## Initially Detecting Project Dependencies Glide can detect the dependencies in use on a project and create an initial `glide.yaml` file for you. This detection can import the configuration from Godep, GPM, Gom, and GB. To do this change into the top level directory for the project and run: $ glide init 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: $ glide up The `up` is short for `update`. This will fetch any dependencies specified in the `glide.yaml` file, walk the dependency tree to make sure any dependencies of the dependencies are fetched, and set them to the proper version. While walking the tree it will make sure versions are set and configuration from Godep, GPM, Gom, and GB is imported. The fetched dependencies are all placed in the `vendor/` folder at the root of the project. The `go` toolchain will use the dependencies here prior to looking in the `GOPATH` or `GOROOT` if you are using Go 1.6+ or Go 1.5 with the Go 1.5 Vendor Experiment enabled. 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: 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 three 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: $ glide install This command does one of two things: * If a `glide.lock` file is present it retrieves, if missing from the `vendor/` folder, the dependency and sets it to the exact version in the `glide.lock` file. The dependencies are fetched and versions set concurrently so this operation is fairly quick. * If there is no `glide.lock` file then an `update` will be performed. If you're not managing the dependency versions for a project but need to install the dependencies you should use the `install` command. ## Adding More Dependencies Glide can help you add more dependencies to the `glide.yaml` file with the `get` command. $ glide get github.com/Masterminds/semver The `get` command is similar to `go get` but instead fetches dependencies into the `vendor/` folder and adds them to the `glide.yaml` file. This command can take one or more dependencies to fetch. The `get` command can also work with versions. $ 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. ================================================ FILE: docs/glide-plugin-example ================================================ #!/bin/bash # You can execute me through Glide by doing the following: # - Copy me to a directory on $PATH. _vendor/bin/ will work just fine. # - Execute `glide plugin-example` # - ??? # - Profit echo "I received arguments '$@'" # This should match the directory from which you executed glide. echo "My current working directory is $(pwd)" # This is the GOPATH for the current glide session. echo "My GOPATH is $GOPATH" # This is the base directory of your project. echo "The project directory is $GLIDE_PROJECT" # This is the location of the glide.yaml file. echo "The Glide YAML is in $GLIDE_YAML" # This is the PATH that the plugin inherited. echo "My PATH is $PATH" ================================================ FILE: docs/glide.lock.md ================================================ # The glide.lock File Where a [`glide.yaml`](glide.yaml.md) file contains the dependencies, versions (including ranges), and other configuration for the local codebase, the related `glide.lock` file contains the complete dependency tree and the revision (commit id) in use. Knowing the complete dependency tree is useful for Glide. For example, when the complete tree is known the `glide install` command can install and set the proper revision for multiple dependencies concurrently. This is a fast operation to reproducibly install the dependencies. The lock file also provides a record of the complete tree, beyond the needs of your codebase, and the revisions used. This is useful for things like audits or detecting what changed in a dependency tree when troubleshooting a problem. The details of this file are not included here as this file should not be edited by hand. If you know how to read the [`glide.yaml`](glide.yaml.md) file you'll be able to generally understand the `glide.lock` file. ================================================ FILE: docs/glide.yaml.md ================================================ # The glide.yaml File The `glide.yaml` file contains information about the project and the dependent packages. Here the elements of the `glide.yaml` file are outlined. package: github.com/Masterminds/glide homepage: https://masterminds.github.io/glide license: MIT owners: - name: Matt Butcher email: technosophos@gmail.com homepage: http://technosophos.com - name: Matt Farina email: matt@mattfarina.com homepage: https://www.mattfarina.com ignore: - appengine excludeDirs: - node_modules import: - package: gopkg.in/yaml.v2 - package: github.com/Masterminds/vcs version: ^1.2.0 repo: git@github.com:Masterminds/vcs vcs: git - package: github.com/codegangsta/cli version: f89effe81c1ece9c5b0fda359ebd9cf65f169a51 - package: github.com/Masterminds/semver version: ^1.0.0 testImport: - package: github.com/arschles/assert These elements are: - `package`: The top level package is the location in the `GOPATH`. This is used for things such as making sure an import isn't also importing the top level package. - `homepage`: To find the place where you can find details about the package or applications. For example, http://k8s.io - license: The license is either an [SPDX license](http://spdx.org/licenses/) string or the filepath to the license. This allows automation and consumers to easily identify the license. - `owners`: The owners is a list of one or more owners for the project. This can be a person or organization and is useful for things like notifying the owners of a security issue without filing a public bug. - `ignore`: A list of packages for Glide to ignore importing. These are package names to ignore rather than directories. - `excludeDirs`: A list of directories in the local codebase to exclude from scanning for dependencies. - `import`: A list of packages to import. Each package can include: - `package`: The name of the package to import and the only non-optional item. Package names follow the same patterns the `go` tool does. That means: - Package names that map to a VCS remote location end in .git, .bzr, .hg, or .svn. For example, `example.com/foo/pkg.git/subpkg`. - GitHub, BitBucket, Launchpad, IBM Bluemix Services, and Go on Google Source are special cases that don't need the VCS extension. - `version`: A semantic version, semantic version range, branch, tag, or commit id to use. For more information see the [versioning documentation](versions.md). - `repo`: If the package name isn't the repo location or this is a private repository it can go here. The package will be checked out from the repo and put where the package name specifies. This allows using forks. - `vcs`: A VCS to use such as git, hg, bzr, or svn. This is only needed when the type cannot be detected from the name. For example, a repo ending in .git or on GitHub can be detected to be Git. For a repo on Bitbucket we can contact the API to discover the type. - `subpackages`: A record of packages being used within a repository. This does not include all packages within a repository but rather those being used. - `os`: A list of operating systems used for filtering. If set it will compare the current runtime OS to the one specified and only fetch the dependency if there is a match. If not set filtering is skipped. The names are the same used in build flags and `GOOS` environment variable. - `arch`: A list of architectures used for filtering. If set it will compare the current runtime architecture to the one specified and only fetch the dependency if there is a match. If not set filtering is skipped. The names are the same used in build flags and `GOARCH` environment variable. - `testImport`: A list of packages used in tests that are not already listed in `import`. Each package has the same details as those listed under import. ================================================ FILE: docs/importing.md ================================================ # Importing Glide has limited support for importing from other formats. **Note:** If you'd like to help build importers, we'd love some pull requests. Just take a look at `cmd/godeps.git`. ## Godeps and Godeps-Git To import from Godeps or Godeps-Git format, run `glide godeps`. This will read the `glide.yaml`, then look for `Godeps` or `Godeps-Git` files to also read. It will then attempt to merge the packages in those files into the current YAML, printing the resulting YAML to standard out. The preferred procedure for merging: ``` $ glide godeps # look at the output and see if it's okay $ glide -q godeps > glide.yaml # Write the merged file ``` ================================================ FILE: docs/index.md ================================================ # Glide: Vendor Package Management for Go [Glide](https://glide.sh) is a package manager for [Go](https://golang.org) that is conceptually similar to package managers for other languages such as Cargo for Rust, NPM for Node.js, Pip for Python, Bundler for Ruby, and so forth. Glide provides the following functionality: * Records dependency information in a `glide.yaml` file. This includes a name, version or version range, version control information for private repos or when the type cannot be detected, and more. * Tracks the specific revision each package is locked to in a `glide.lock` file. This enables reproducibly fetching the dependency tree. * Works with Semantic Versions and Semantic Version ranges. * Supports Git, Bzr, HG, and SVN. These are the same version control systems supported by `go get`. * Utilizes `vendor/` directories, known as the Vendor Experiment, so that different projects can have differing versions of the same dependencies. * Allows for aliasing packages which is useful for working with forks. * Import configuration from Godep, GPM, Gom, and GB. ## Installing Glide There are a few ways to install Glide. 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. ================================================ FILE: docs/plugins.md ================================================ # Glide Plugins Glide supports a simple plugin system similar to Git. ## Existing Plugins Some plugins exist today for Glide including: * [glide-vc](https://github.com/sgotti/glide-vc) - The vendor cleaner allows you to strip files not needed for building your application from the `vendor/` directory. * [glide-brew](https://github.com/heewa/glide-brew) - Convert Go deps managed by glide to Homebrew resources to help you make brew formulas for you Go programs. * [glide-hash](https://github.com/mattfarina/glide-hash) - Generates a hash of the `glide.yaml` file compatible with Glides internal hash. * [glide-cleanup](https://github.com/ngdinhtoan/glide-cleanup) - Removing unused packages from the `glide.yaml` file. * [glide-pin](https://github.com/multiplay/glide-pin) - Take all dependencies from the `glide.lock` and pin them explicitly in the `glide.yaml` file. _Note, to add plugins to this list please create a pull request._ ## How Plugins Work When Glide encounters a subcommand that it does not know, it will try to delegate it to another executable according to the following rules. Example: ``` $ glide install # We know this command, so we execute it $ glide foo # We don't know this command, so we look for a suitable # plugin. ``` In the example above, when glide receives the command `foo`, which it does not know, it will do the following: 1. Transform the name from `foo` to `glide-foo` 2. Look on the system `$PATH` for `glide-foo`. If it finds a program by that name, execute it... 3. Or else, look at the current project's root for `glide-foo`. (That is, look in the same directory as `glide.yaml`). If found, execute it. 4. If no suitable command is found, exit with an error. ## Writing a Glide Plugin A Glide plugin can be written in any language you wish, provided that it can be executed from the command line as a subprocess of Glide. The example included with Glide is a simple Bash script. We could just as easily write Go, Python, Perl, or even Java code (with a wrapper) to execute. A Glide plugin must be in one of two locations: 1. Somewhere on the PATH 2. In the same directory as `glide.yaml` It is recommended that system-wide Glide plugins go in `/usr/local/bin` or `$GOPATH/bin` while project-specific plugins go in the same directory as `glide.yaml`. ### Arguments and Flags Say Glide is executed like this: ``` $ glide foo -name=Matt myfile.txt ``` Glide will interpret this as a request to execute `glide-foo` with the arguments `-name=Matt myfile.txt`. It will not attempt to interpret those arguments or modify them in any way. Hypothetically, if Glide had a `-x` flag of its own, you could call this: ``` $ glide -x foo -name=Matt myfile.txt ``` In this case, glide would interpret and swollow the -x and pass the rest on to `glide-foo` as in the example above. ## Example Plugin File: glide-foo ```bash #!/bin/bash echo "Hello" ``` ================================================ FILE: docs/resolving-imports.md ================================================ # Resolving Imports Glide scans an applications codebase to discover the projects to manage in the `vendor/` directory. This happens in a few different ways. Knowing how this works can help you understand what Glide is doing. ## At Initialization When you run `glide create` or `glide init` to create a `glide.yaml` file for a codebase Glide will scan your codebase to identify the imports. It does this by walking the filesystem to identify packages. In each package it reads the imports within the Go files. From this it will attempt to figure out the external packages. External packages are grouped by the root version control system repo with their sub-packages listed underneath. Figuring out the root version control repo compared with the packages underneath it follows the same rules for the `go` tool. 1. GitHub, Bitbucket, Launchpad, IBM Jazz, and go.googlesource.com are evaluated with special rules. We know or can talk to an API to learn about these repos. 2. If the package associated with the repo ends in `.git`, `.hg`, `.bzr`, or `.svn` this is used to determine the root and the type of version control system. 3. If the rules don't provide an answer a `go get` request occurs to try and lookup the information. Again, this is the same way `go` tries to determine an external location when you use `go get`. If the project has dependency configuration stored in a Godep, GPM, Gom, or GB file that information will be used to populate the version within the `glide.yaml` file. ## At Update When `glide update`, `glide up`, `glide get`, or `glide install` (when no `glide.lock` is present) Glide will attempt to discover the complete dependency tree. That is all dependencies including dependencies of dependencies of dependencies. ### The Default Option The default method is to walk the referenced import tree. The resolver starts by scanning the local application to get a list of imports. Then it looks at the specific package imports, scans the imported package for imports, and repeats the lookup cycle until the complete tree has been fetched. That means that only imports referenced in the source are fetched. When a version control repo is fetched it does fetch the complete repo. But, it doesn't scan all the packages in the repo for dependencies. Instead, only the packages referenced in the tree are scanned with the imports being followed. Along the way configuration stored in Glide, Godep, GPM, Gom, and GB files are used to work out the version to set and fetched repos to. The first version found while walking the import tree wins. ### All Possible Dependencies Using the `--all-dependencies` flag on `glide update` will change the behavior of the scan. Instead of walking the import tree it walks the filesystem and fetches all possible packages referenced everywhere. This downloads all packages in the tree. Even those not referenced in an applications source or in support of the applications imports. As in other cases, Glide, Godep, GPM, Gom, and GB files are used to set the version of the fetched repo. ================================================ FILE: docs/vendor.md ================================================ # Vendor Directories With the release of Go 1.5 the `vendor/` directory was added to the resolution locations for a dependent package in addition to the `GOPATH` and `GOROOT`. Prior to Go 1.6 you needed to opt-in before Go would look there by setting the environment variable `GO15VENDOREXPERIMENT=1`. In Go 1.6 this is an opt-out feature. _Note, even if you use the `vendor/` directories your codebase needs to be inside the `GOPATH`. With the `go` toolchain there is no escaping the `GOPATH`._ The resolution locations for a dependent package are: * The `vendor/` directory within the current package. * Walk up the directory tree looking for the package in a parents `vendor/` directory. * Look for the package in the `GOPATH`. * Use the package in the `GOROOT` (where the standard library package reside) if present. ## Recommendations Having worked with the `vendor/` directories since they were first released we've come to some conclusions and recommendations. Glide tries to help you with these. 1. Libraries (codebases without a `main` package) should not store outside packages in a `vendor/` folder in their VCS unless they have a specific reason and understand why they're doing it. 2. In applications (codebases with a `main` package) there should only be one `vendor/` directory at the top level of the codebase. There are some important reasons for these recommendations. * Each instance of a package, even the same package at the same version, in the directory structure will be in the resulting binaries. If everyone stores their own dependencies separately this will quickly lead to **binary bloat**. * Instances of a type created from a package in one location are **not compatible** with the same package, even at the exact same version, in another location. [You can see for yourself](https://github.com/mattfarina/golang-broken-vendor). That means loggers, database connections, and other shared instances won't work. Because of this Glide flattens the dependency tree into a single top level `vendor/` directory. If a package happens to have some dependencies in their own `vendor/` folder the `go` tool will properly resolve that version. ## Why Use A `vendor` Directory? If we already have the `GOPATH` to store packages why is there a need for a `vendor/` directory? This is a perfectly valid question. What if multiple applications in the `GOPATH` use different versions of the same package? This is a valid problem that's both been encountered in Go applications and widely seen in languages that have been around for a lot longer. The `vendor/` directory allows differing codebases to have their own version available without having to be concerned with another codebase that needs a different version interfering with the version it needs. It provides a level of separation for each project. ================================================ FILE: docs/versions.md ================================================ # Versions and Ranges Glide supports [Semantic Versions](http://semver.org), SemVer ranges, branches, tags, and commit ids as versions. ## Basic Ranges A simple range is in the form `> 1.2.3`. This tells Glide to use the latest versions that's after `1.2.3`. Glide has support for the following operators: * `=`: equal (aliased to no operator) * `!=`: not equal * `>`: greater than * `<`: less than * `>=`: greater than or equal to * `<=`: less than or equal to These can be combined. A `,` is an and operator and a `||` is an or operator. The or operators cause groups of and operators to be checked. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"`. ## Hyphen Ranges There are multiple shortcuts to handle ranges and the first is hyphens ranges. These look like: * `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5` * `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5` ## Wildcards In Comparisons The `x`, `X`, and `*` characters can be used as a wildcard character. This works for all comparison operators. When used on the `=` operator it falls back to the patch level comparison (see tilde below). For example, * `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` * `>= 1.2.x` is equivalent to `>= 1.2.0` * `<= 2.x` is equivalent to `< 3` * `*` is equivalent to `>= 0.0.0` ## Tilde Range Comparisons (Patch) The tilde (`~`) comparison operator is for patch level ranges when a minor version is specified and major level changes when the minor number is missing. For example, * `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0` * `~1` is equivalent to `>= 1, < 2` * `~2.3` is equivalent to `>= 2.3, < 2.4` * `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` * `~1.x` is equivalent to `>= 1, < 2` ## Caret Range Comparisons (Major) The caret (`^`) comparison operator is for major level changes. This is useful when comparisons of API versions as a major change is API breaking. For example, * `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` * `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` * `^2.3` is equivalent to `>= 2.3, < 3` * `^2.x` is equivalent to `>= 2.0.0, < 3` ================================================ FILE: gb/gb.go ================================================ package gb import ( "encoding/json" "os" "path/filepath" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/glide/util" ) // Has returns true if this dir has a GB-flavored manifest file. func Has(dir string) bool { path := filepath.Join(dir, "vendor/manifest") _, err := os.Stat(path) return err == nil } // Parse parses a GB-flavored manifest file. func Parse(dir string) ([]*cfg.Dependency, error) { path := filepath.Join(dir, "vendor/manifest") if fi, err := os.Stat(path); err != nil || fi.IsDir() { return []*cfg.Dependency{}, nil } msg.Info("Found GB manifest file in %s", gpath.StripBasepath(dir)) msg.Info("--> Parsing GB metadata...") buf := []*cfg.Dependency{} file, err := os.Open(path) if err != nil { return buf, err } defer file.Close() man := Manifest{} dec := json.NewDecoder(file) if err := dec.Decode(&man); err != nil { return buf, err } seen := map[string]bool{} for _, d := range man.Dependencies { pkg, sub := util.NormalizeName(d.Importpath) if _, ok := seen[pkg]; ok { if len(sub) == 0 { continue } for _, dep := range buf { if dep.Name == pkg { dep.Subpackages = append(dep.Subpackages, sub) } } } else { seen[pkg] = true dep := &cfg.Dependency{ Name: pkg, Reference: d.Revision, Repository: d.Repository, } if len(sub) > 0 { dep.Subpackages = []string{sub} } buf = append(buf, dep) } } return buf, nil } ================================================ FILE: gb/manifest.go ================================================ // Package gb provides compatibility with GB manifests. package gb // This is lifted wholesale from GB's `vendor/manifest.go` file. // // gb's license is MIT-style. // Manifest represents the GB manifest file type Manifest struct { Version int `json:"version"` Dependencies []Dependency `json:"dependencies"` } // Dependency represents an individual dependency in the GB manifest file type Dependency struct { Importpath string `json:"importpath"` Repository string `json:"repository"` Revision string `json:"revision"` Branch string `json:"branch"` Path string `json:"path,omitempty"` } ================================================ FILE: glide.go ================================================ // Glide is a command line utility that manages Go project dependencies. // // Configuration 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 // - 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. // // For more information use the `glide help` command or see https://glide.sh package main import ( "path/filepath" "github.com/Masterminds/glide/action" "github.com/Masterminds/glide/cache" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/glide/repo" "github.com/Masterminds/glide/util" "github.com/codegangsta/cli" "fmt" "os" ) var version = "0.13.4-dev" 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: package: github.com/Masterminds/glide imports: - package: github.com/Masterminds/cookoo version: 1.1.0 - package: github.com/kylelemons/go-gypsy subpackages: - yaml For more details on the 'glide.yaml' files see the documentation at https://glide.sh/docs/glide.yaml ` // VendorDir default vendor directory name var VendorDir = "vendor" func main() { app := cli.NewApp() app.Name = "glide" app.Usage = usage app.Version = version app.Flags = []cli.Flag{ cli.StringFlag{ Name: "yaml, y", Value: "glide.yaml", Usage: "Set a YAML configuration file.", }, cli.BoolFlag{ Name: "quiet, q", Usage: "Quiet (no info or debug messages)", }, cli.BoolFlag{ Name: "debug", Usage: "Print debug verbose informational messages", }, cli.StringFlag{ Name: "home", Value: gpath.Home(), 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", }, } app.CommandNotFound = func(c *cli.Context, command string) { // TODO: Set some useful env vars. action.Plugin(command, os.Args) } app.Before = startup app.After = shutdown app.Commands = commands() // Detect errors from the Before and After calls and exit on them. if err := app.Run(os.Args); err != nil { msg.Err(err.Error()) os.Exit(1) } // If there was an Error message exit non-zero. if msg.HasErrored() { m := msg.Color(msg.Red, "An Error has occurred") msg.Msg(m) os.Exit(2) } } func commands() []cli.Command { return []cli.Command{ { Name: "create", ShortName: "init", Usage: "Initialize a new project, creating a glide.yaml file", Description: `This command starts from a project without Glide and sets it up. It generates a glide.yaml file, parsing your codebase to guess the dependencies to include. Once this step is done you may edit the glide.yaml file to update imported dependency properties such as the version or version range to include. To fetch the dependencies you may run 'glide install'.`, Flags: []cli.Flag{ cli.BoolFlag{ Name: "skip-import", Usage: "When initializing skip importing from other package managers.", }, cli.BoolFlag{ Name: "non-interactive", Usage: "Disable interactive prompts.", }, }, Action: func(c *cli.Context) error { action.Create(".", c.Bool("skip-import"), c.Bool("non-interactive")) return nil }, }, { Name: "config-wizard", ShortName: "cw", Usage: "Wizard that makes optional suggestions to improve config in a glide.yaml file.", 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) error { action.ConfigWizard(".") return nil }, }, { Name: "get", Usage: "Install one or more packages into `vendor/` and add dependency to glide.yaml.", 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 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, 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 versions for the dependencies of this dependency (transitive ones). This will generate an updated glide.lock file with specific locked versions to use. 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.`, Flags: []cli.Flag{ cli.BoolFlag{ Name: "test", Usage: "Add test dependencies.", }, cli.BoolFlag{ Name: "insecure", Usage: "Use http:// rather than https:// to retrieve packages.", }, cli.BoolFlag{ Name: "no-recursive, quick", Usage: "Disable updating dependencies' dependencies.", }, 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: "all-dependencies", 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.", Hidden: true, }, cli.BoolFlag{ 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.", Hidden: true, }, cli.BoolFlag{ 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.", Hidden: true, }, cli.BoolFlag{ Name: "strip-vendor, v", Usage: "Removes nested vendor and Godeps/_workspace directories.", }, cli.BoolFlag{ Name: "non-interactive", Usage: "Disable interactive prompts.", }, cli.BoolFlag{ Name: "skip-test", Usage: "Resolve dependencies in test files.", }, }, 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 --file 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 { fmt.Println("Oops! Package name is required.") os.Exit(1) } if c.Bool("resolve-current") { util.ResolveCurrent = true msg.Warn("Only resolving dependencies for the current OS/Arch.") } inst := repo.NewInstaller() inst.Force = c.Bool("force") 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-vendor"), c.Bool("non-interactive"), c.Bool("test")) return nil }, }, { Name: "remove", 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 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) error { if len(c.Args()) < 1 { fmt.Println("Oops! At least one package name is required.") os.Exit(1) } if c.Bool("delete") { // FIXME: Implement this in the installer. fmt.Println("Delete is not currently implemented.") } inst := repo.NewInstaller() inst.Force = c.Bool("force") packages := []string(c.Args()) action.Remove(packages, inst) return nil }, }, { Name: "import", Usage: "Import files from other dependency management systems.", Subcommands: []cli.Command{ { Name: "godep", Usage: "Import Godep's Godeps.json files and display the would-be yaml file", Flags: []cli.Flag{ cli.StringFlag{ Name: "file, f", Usage: "Save all of the discovered dependencies to a Glide YAML file.", }, }, Action: func(c *cli.Context) error { action.ImportGodep(c.String("file")) return nil }, }, { Name: "gpm", Usage: "Import GPM's Godeps and Godeps-Git files and display the would-be yaml file", Flags: []cli.Flag{ cli.StringFlag{ Name: "file, f", Usage: "Save all of the discovered dependencies to a Glide YAML file.", }, }, Action: func(c *cli.Context) error { action.ImportGPM(c.String("file")) return nil }, }, { Name: "gb", Usage: "Import gb's manifest file and display the would-be yaml file", Flags: []cli.Flag{ cli.StringFlag{ Name: "file, f", Usage: "Save all of the discovered dependencies to a Glide YAML file.", }, }, Action: func(c *cli.Context) error { action.ImportGB(c.String("file")) return nil }, }, { Name: "gom", Usage: "Import Gomfile and display the would-be yaml file", Flags: []cli.Flag{ cli.StringFlag{ Name: "file, f", Usage: "Save all of the discovered dependencies to a Glide YAML file.", }, }, Action: func(c *cli.Context) error { action.ImportGom(c.String("file")) return nil }, }, }, }, { 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) error { action.Name() return nil }, }, { Name: "novendor", ShortName: "nv", Usage: "List all non-vendor paths in a directory.", Description: `Given a directory, list all the relevant Go paths that are not vendored. Example: $ go test $(glide novendor)`, Flags: []cli.Flag{ cli.StringFlag{ Name: "dir,d", Usage: "Specify a directory to run novendor against.", Value: ".", }, cli.BoolFlag{ Name: "no-subdir,x", Usage: "Specify this to prevent nv from append '/...' to all directories.", }, }, 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: `(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) error { action.Rebuild() return nil }, }, { Name: "install", ShortName: "i", Usage: "Install a project's dependencies", Description: `This uses the native VCS of each package to install the appropriate version. There are two ways a project's dependencies can be installed. When there is a glide.yaml file defining the dependencies but no lock file (glide.lock) the dependencies are installed using the "update" command and a glide.lock file is generated pinning all dependencies. If a glide.lock file is already present the dependencies are installed or updated from the lock file.`, Flags: []cli.Flag{ cli.BoolFlag{ 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/.", Hidden: true, }, cli.StringFlag{ 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.", Hidden: true, }, cli.BoolFlag{ 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.", Hidden: true, }, cli.BoolFlag{ 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.", }, cli.BoolFlag{ Name: "skip-test", Usage: "Resolve dependencies in test files.", }, }, 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.Home = c.GlobalString("home") installer.ResolveTest = !c.Bool("skip-test") action.Install(installer, c.Bool("strip-vendor")) return nil }, }, { Name: "update", ShortName: "up", Usage: "Update a project's dependencies", 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 use the information contained there. Those dependencies are maintained in 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, 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. 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.`, Flags: []cli.Flag{ cli.BoolFlag{ Name: "delete", Usage: "Delete vendor packages not specified in config.", Hidden: true, }, cli.BoolFlag{ Name: "no-recursive, quick", Usage: "Disable updating dependencies' dependencies. Only update things in glide.yaml.", }, 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: "all-dependencies", 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.", Hidden: true, }, cli.StringFlag{ 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.", Hidden: true, }, cli.BoolFlag{ 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.", 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.", Hidden: true, }, cli.BoolFlag{ Name: "strip-vendor, v", Usage: "Removes nested vendor and Godeps/_workspace directories.", }, cli.BoolFlag{ Name: "skip-test", Usage: "Resolve dependencies in test files.", }, }, 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") { util.ResolveCurrent = true msg.Warn("Only resolving dependencies for the current OS/Arch") } installer := repo.NewInstaller() installer.Force = c.Bool("force") installer.ResolveAllFiles = c.Bool("all-dependencies") installer.Home = c.GlobalString("home") installer.ResolveTest = !c.Bool("skip-test") action.Update(installer, c.Bool("no-recursive"), c.Bool("strip-vendor")) return nil }, }, { Name: "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. 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 }, }, { Name: "list", Usage: "List prints all dependencies that the present code references.", Description: `List scans your code and lists all of the packages that are used. It does not use the glide.yaml. Instead, it inspects the code to determine what packages are imported. 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) error { action.List(".", true, c.String("output")) return nil }, Flags: []cli.Flag{ cli.StringFlag{ Name: "output, o", Usage: "Output format. One of: json|json-pretty|text", Value: "text", }, }, }, { Name: "info", Usage: "Info prints information about this project", Flags: []cli.Flag{ cli.StringFlag{ Name: "format, f", Usage: `Format of the information wanted (required).`, }, }, Description: `A format containing the text with replacement variables has to be passed in. Those variables are: %n - name %d - description %h - homepage %l - license For example, given a project with the following glide.yaml: package: foo homepage: https://example.com license: MIT description: Some example description Then running the following commands: glide info -f %n prints 'foo' glide info -f "License: %l" prints 'License: MIT' glide info -f "%n - %d - %h - %l" prints 'foo - Some example description - https://example.com - MIT'`, 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) error { action.CacheClear() return nil }, }, { Name: "about", Usage: "Learn about Glide", 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 a mirrors.yaml file in your GLIDE_HOME. The three commands to manage 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 a 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)) }, }, }, }, } } // startup sets up the base environment. // // It does not assume the presence of a Glide.yaml file or vendor/ directory, // so it can be used by any Glide command. func startup(c *cli.Context) error { action.Debug(c.Bool("debug")) action.NoColor(c.Bool("no-color")) action.Quiet(c.Bool("quiet")) action.Init(c.String("yaml"), c.String("home")) action.EnsureGoVendor() gpath.Tmp = c.String("tmp") return nil } func shutdown(c *cli.Context) error { cache.SystemUnlock() return nil } // Get the path to the glide.yaml file. // // This returns the name of the path, even if the file does not exist. The value // may be set by the user, or it may be the default. func glidefile(c *cli.Context) string { path := c.String("file") if path == "" { // For now, we construct a basic assumption. In the future, we could // traverse backward to see if a glide.yaml exists in a parent. path = "./glide.yaml" } a, err := filepath.Abs(path) if err != nil { // Underlying fs didn't provide working dir. return path } return a } ================================================ FILE: glide.yaml ================================================ package: github.com/Masterminds/glide homepage: https://glide.sh license: MIT owners: - name: Matt Butcher email: technosophos@gmail.com homepage: http://technosophos.com/ - name: Matt Farina email: matt@mattfarina.com homepage: https://www.mattfarina.com/ import: - package: gopkg.in/yaml.v2 - package: github.com/Masterminds/vcs version: ^1.13.1 - package: github.com/codegangsta/cli version: ^1.16.0 - package: github.com/Masterminds/semver version: ^1.4.0 - package: github.com/mitchellh/go-homedir ================================================ FILE: glide_test.go ================================================ package main import ( "testing" ) func TestCommandsNonEmpty(t *testing.T) { commands := commands() if len(commands) == 0 { t.Fail() } } ================================================ FILE: godep/godep.go ================================================ // Package godep provides basic importing of Godep dependencies. // // This is not a complete implementation of Godep. package godep import ( "encoding/json" "os" "path/filepath" "strings" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/glide/util" ) // This file contains commands for working with Godep. // The Godeps struct from Godep. // // https://raw.githubusercontent.com/tools/godep/master/dep.go // // We had to copy this because it's in the package main for Godep. type Godeps struct { ImportPath string GoVersion string Packages []string `json:",omitempty"` // Arguments to save, if any. Deps []Dependency outerRoot string } // Dependency is a modified version of Godep's Dependency struct. // It drops all of the unexported fields. type Dependency struct { ImportPath string Comment string `json:",omitempty"` // Description of commit, if present. Rev string // VCS-specific commit ID. } // Has is a command to detect if a package contains a Godeps.json file. func Has(dir string) bool { path := filepath.Join(dir, "Godeps/Godeps.json") _, err := os.Stat(path) return err == nil } // Parse parses a Godep's Godeps file. // // It returns the contents as a dependency array. func Parse(dir string) ([]*cfg.Dependency, error) { path := filepath.Join(dir, "Godeps/Godeps.json") if _, err := os.Stat(path); err != nil { return []*cfg.Dependency{}, nil } msg.Info("Found Godeps.json file in %s", gpath.StripBasepath(dir)) msg.Info("--> Parsing Godeps metadata...") buf := []*cfg.Dependency{} godeps := &Godeps{} // Get a handle to the file. file, err := os.Open(path) if err != nil { return buf, err } defer file.Close() dec := json.NewDecoder(file) if err := dec.Decode(godeps); err != nil { return buf, err } seen := map[string]bool{} for _, d := range godeps.Deps { pkg, sub := util.NormalizeName(d.ImportPath) if _, ok := seen[pkg]; ok { if len(sub) == 0 { continue } // Modify existing dep with additional subpackages. for _, dep := range buf { if dep.Name == pkg { dep.Subpackages = append(dep.Subpackages, sub) } } } else { seen[pkg] = true dep := &cfg.Dependency{Name: pkg, Reference: d.Rev} if sub != "" { dep.Subpackages = []string{sub} } buf = append(buf, dep) } } return buf, nil } // RemoveGodepSubpackages strips subpackages from a cfg.Config dependencies that // contain "Godeps/_workspace/src" as part of the path. func RemoveGodepSubpackages(c *cfg.Config) *cfg.Config { for _, d := range c.Imports { n := []string{} for _, v := range d.Subpackages { if !strings.HasPrefix(v, "Godeps/_workspace/src") { n = append(n, v) } } d.Subpackages = n } for _, d := range c.DevImports { n := []string{} for _, v := range d.Subpackages { if !strings.HasPrefix(v, "Godeps/_workspace/src") { n = append(n, v) } } d.Subpackages = n } return c } ================================================ FILE: godep/strip/strip.go ================================================ // Package strip removes Godeps/_workspace and undoes the Godep rewrites. This // essentially removes the old style (pre-vendor) Godep vendoring. // // Note, this functionality is deprecated. Once more projects use the Godep // support for the core vendoring this will no longer be needed. package strip import ( "bytes" "go/ast" "go/parser" "go/printer" "go/token" "os" "path/filepath" "strconv" "strings" "github.com/Masterminds/glide/msg" ) var godepMark = map[string]bool{} var vPath = "vendor" // GodepWorkspace removes any Godeps/_workspace directories and makes sure // any rewrites are undone. // Note, this is not concuccency safe. func GodepWorkspace(v string) error { vPath = v if _, err := os.Stat(vPath); err != nil { if os.IsNotExist(err) { msg.Debug("Vendor directory does not exist.") } return err } err := filepath.Walk(vPath, stripGodepWorkspaceHandler) if err != nil { return err } // Walk the marked projects to make sure rewrites are undone. for k := range godepMark { msg.Info("Removing Godep rewrites for %s", k) err := filepath.Walk(k, rewriteGodepfilesHandler) if err != nil { return err } } return nil } func stripGodepWorkspaceHandler(path string, info os.FileInfo, err error) error { // Skip the base vendor directory if path == vPath { return nil } name := info.Name() p := filepath.Dir(path) pn := filepath.Base(p) if name == "_workspace" && pn == "Godeps" { if _, err := os.Stat(path); err == nil { if info.IsDir() { // Marking this location to make sure rewrites are undone. pp := filepath.Dir(p) godepMark[pp] = true msg.Info("Removing: %s", path) if err := os.RemoveAll(path); err != nil { return err } return filepath.SkipDir } msg.Debug("%s is not a directory. Skipping removal", path) return nil } } return nil } func rewriteGodepfilesHandler(path string, info os.FileInfo, err error) error { name := info.Name() if info.IsDir() { if name == "testdata" || name == "vendor" { return filepath.SkipDir } return nil } if e := filepath.Ext(path); e != ".go" { return nil } fset := token.NewFileSet() f, err := parser.ParseFile(fset, path, nil, parser.ParseComments) if err != nil { return err } var changed bool for _, s := range f.Imports { n, err := strconv.Unquote(s.Path.Value) if err != nil { return err } q := rewriteGodepImport(n) if q != name { s.Path.Value = strconv.Quote(q) changed = true } } if !changed { return nil } printerConfig := &printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8} var buffer bytes.Buffer if err = printerConfig.Fprint(&buffer, fset, f); err != nil { return err } fset = token.NewFileSet() f, err = parser.ParseFile(fset, name, &buffer, parser.ParseComments) ast.SortImports(fset, f) tpath := path + ".temp" t, err := os.Create(tpath) if err != nil { return err } if err = printerConfig.Fprint(t, fset, f); err != nil { return err } if err = t.Close(); err != nil { return err } msg.Debug("Rewriting Godep imports for %s", path) // This is required before the rename on windows. if err = os.Remove(path); err != nil { return err } return os.Rename(tpath, path) } func rewriteGodepImport(n string) string { if !strings.Contains(n, "Godeps/_workspace/src") { return n } i := strings.LastIndex(n, "Godeps/_workspace/src") return strings.TrimPrefix(n[i:], "Godeps/_workspace/src/") } ================================================ FILE: godep/strip/strip_test.go ================================================ package strip import "testing" func TestRewriteGodepImport(t *testing.T) { tests := map[string]string{ "github.com/Masterminds/glide/action": "github.com/Masterminds/glide/action", "github.com/tools/godep/Godeps/_workspace/src/github.com/kr/fs": "github.com/kr/fs", } for k, v := range tests { o := rewriteGodepImport(k) if o != v { t.Errorf("Incorrect Godep import path rewritten %s: %s", v, o) } } } ================================================ FILE: gom/gom.go ================================================ package gom import ( "errors" "os" "path/filepath" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/glide/util" ) // Has returns true if this dir has a Gomfile. func Has(dir string) bool { path := filepath.Join(dir, "Gomfile") fi, err := os.Stat(path) return err == nil && !fi.IsDir() } // Parse parses a Gomfile. func Parse(dir string) ([]*cfg.Dependency, error) { path := filepath.Join(dir, "Gomfile") if fi, err := os.Stat(path); err != nil || fi.IsDir() { return []*cfg.Dependency{}, nil } msg.Info("Found Gomfile in %s", gpath.StripBasepath(dir)) msg.Info("--> Parsing Gomfile metadata...") buf := []*cfg.Dependency{} goms, err := parseGomfile(path) if err != nil { return []*cfg.Dependency{}, err } for _, gom := range goms { // Do we need to skip this dependency? if val, ok := gom.options["skipdep"]; ok && val.(string) == "true" { continue } // Check for custom cloning command if _, ok := gom.options["command"]; ok { return []*cfg.Dependency{}, errors.New("Glide does not support custom Gomfile commands") } // Check for groups/environments if val, ok := gom.options["group"]; ok { groups := toStringSlice(val) if !stringsContain(groups, "development") && !stringsContain(groups, "production") { // right now we only support development and production msg.Info("Skipping dependency '%s' because it isn't in the development or production group", gom.name) continue } } pkg, sub := util.NormalizeName(gom.name) dep := &cfg.Dependency{ Name: pkg, } if len(sub) > 0 { dep.Subpackages = []string{sub} } // Check for a specific revision if val, ok := gom.options["commit"]; ok { dep.Reference = val.(string) } if val, ok := gom.options["tag"]; ok { dep.Reference = val.(string) } if val, ok := gom.options["branch"]; ok { dep.Reference = val.(string) } // Parse goos and goarch if val, ok := gom.options["goos"]; ok { dep.Os = toStringSlice(val) } if val, ok := gom.options["goarch"]; ok { dep.Arch = toStringSlice(val) } buf = append(buf, dep) } return buf, nil } func stringsContain(v []string, key string) bool { for _, s := range v { if s == key { return true } } return false } func toStringSlice(v interface{}) []string { if v, ok := v.(string); ok { return []string{v} } if v, ok := v.([]string); ok { return v } return []string{} } ================================================ FILE: gom/parser.go ================================================ package gom // This is copied + slightly adapted from gom's `gomfile.go` file. // // gom's license is MIT-style. import ( "bufio" "fmt" "io" "os" "regexp" "strings" ) var qx = `'[^']*'|"[^"]*"` var kx = `:[a-z][a-z0-9_]*` var ax = `(?:\s*` + kx + `\s*|,\s*` + kx + `\s*)` var reGroup = regexp.MustCompile(`\s*group\s+((?:` + kx + `\s*|,\s*` + kx + `\s*)*)\s*do\s*$`) var reEnd = regexp.MustCompile(`\s*end\s*$`) var reGom = regexp.MustCompile(`^\s*gom\s+(` + qx + `)\s*((?:,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*))*)$`) var reOptions = regexp.MustCompile(`(,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*)\s*)`) func unquote(name string) string { name = strings.TrimSpace(name) if len(name) > 2 { if (name[0] == '\'' && name[len(name)-1] == '\'') || (name[0] == '"' && name[len(name)-1] == '"') { return name[1 : len(name)-1] } } return name } func parseOptions(line string, options map[string]interface{}) { ss := reOptions.FindAllStringSubmatch(line, -1) reA := regexp.MustCompile(ax) for _, s := range ss { kvs := strings.SplitN(strings.TrimSpace(s[0])[1:], "=>", 2) kvs[0], kvs[1] = strings.TrimSpace(kvs[0]), strings.TrimSpace(kvs[1]) if kvs[1][0] == '[' { as := reA.FindAllStringSubmatch(kvs[1][1:len(kvs[1])-1], -1) a := []string{} for i := range as { it := strings.TrimSpace(as[i][0]) if strings.HasPrefix(it, ",") { it = strings.TrimSpace(it[1:]) } if strings.HasPrefix(it, ":") { it = strings.TrimSpace(it[1:]) } a = append(a, it) } options[kvs[0][1:]] = a } else { options[kvs[0][1:]] = unquote(kvs[1]) } } } // Gom represents configuration from Gom. type Gom struct { name string options map[string]interface{} } func parseGomfile(filename string) ([]Gom, error) { f, err := os.Open(filename + ".lock") if err != nil { f, err = os.Open(filename) if err != nil { return nil, err } } br := bufio.NewReader(f) goms := make([]Gom, 0) n := 0 skip := 0 valid := true var envs []string for { n++ lb, _, err := br.ReadLine() if err != nil { if err == io.EOF { return goms, nil } return nil, err } line := strings.TrimSpace(string(lb)) if line == "" || strings.HasPrefix(line, "#") { continue } name := "" options := make(map[string]interface{}) var items []string if reGroup.MatchString(line) { envs = strings.Split(reGroup.FindStringSubmatch(line)[1], ",") for i := range envs { envs[i] = strings.TrimSpace(envs[i])[1:] } valid = true continue } else if reEnd.MatchString(line) { if !valid { skip-- if skip < 0 { return nil, fmt.Errorf("Syntax Error at line %d", n) } } valid = false envs = nil continue } else if skip > 0 { continue } else if reGom.MatchString(line) { items = reGom.FindStringSubmatch(line)[1:] name = unquote(items[0]) parseOptions(items[1], options) } else { return nil, fmt.Errorf("Syntax Error at line %d", n) } if envs != nil { options["group"] = envs } goms = append(goms, Gom{name, options}) } } ================================================ FILE: gpm/gpm.go ================================================ // Package gpm reads GPM's Godeps files. // // It is not a complete implementaton of GPM. package gpm import ( "bufio" "os" "path/filepath" "strings" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" ) // Has indicates whether a Godeps file exists. func Has(dir string) bool { path := filepath.Join(dir, "Godeps") _, err := os.Stat(path) return err == nil } // Parse parses a GPM-flavored Godeps file. func Parse(dir string) ([]*cfg.Dependency, error) { path := filepath.Join(dir, "Godeps") if i, err := os.Stat(path); err != nil { return []*cfg.Dependency{}, nil } else if i.IsDir() { msg.Info("Godeps is a directory. This is probably a Godep project.\n") return []*cfg.Dependency{}, nil } msg.Info("Found Godeps file in %s", gpath.StripBasepath(dir)) msg.Info("--> Parsing GPM metadata...") buf := []*cfg.Dependency{} file, err := os.Open(path) if err != nil { return buf, err } scanner := bufio.NewScanner(file) for scanner.Scan() { parts, ok := parseGodepsLine(scanner.Text()) if ok { dep := &cfg.Dependency{Name: parts[0]} if len(parts) > 1 { dep.Reference = parts[1] } buf = append(buf, dep) } } if err := scanner.Err(); err != nil { msg.Warn("Scan failed: %s\n", err) return buf, err } return buf, nil } func parseGodepsLine(line string) ([]string, bool) { line = strings.TrimSpace(line) if len(line) == 0 || strings.HasPrefix(line, "#") { return []string{}, false } return strings.Fields(line), true } ================================================ FILE: importer/importer.go ================================================ // Package importer imports dependency configuration from Glide, Godep, GPM, GB and gom package importer import ( "io/ioutil" "os" "path/filepath" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/gb" "github.com/Masterminds/glide/godep" "github.com/Masterminds/glide/gom" "github.com/Masterminds/glide/gpm" ) var i = &DefaultImporter{} // Import uses the DefaultImporter to import from Glide, Godep, GPM, GB and gom. func Import(path string) (bool, []*cfg.Dependency, error) { return i.Import(path) } // Importer enables importing depenency configuration. type Importer interface { // Import imports dependency configuration. It returns: // - A bool if any configuration was found. // - []*cfg.Dependency containing dependency configuration if any is found. // - An error if one was reported. Import(path string) (bool, []*cfg.Dependency, error) } // DefaultImporter imports from Glide, Godep, GPM, GB and gom. type DefaultImporter struct{} // Import tries to import configuration from Glide, Godep, GPM, GB and gom. func (d *DefaultImporter) Import(path string) (bool, []*cfg.Dependency, error) { // Try importing from Glide first. p := filepath.Join(path, "glide.yaml") if _, err := os.Stat(p); err == nil { // We found glide configuration. yml, err := ioutil.ReadFile(p) if err != nil { return false, []*cfg.Dependency{}, err } conf, err := cfg.ConfigFromYaml(yml) if err != nil { return false, []*cfg.Dependency{}, err } return true, conf.Imports, nil } // Try importing from Godep if godep.Has(path) { deps, err := godep.Parse(path) if err != nil { return false, []*cfg.Dependency{}, err } return true, deps, nil } // Try importing from GPM if gpm.Has(path) { deps, err := gpm.Parse(path) if err != nil { return false, []*cfg.Dependency{}, err } return true, deps, nil } // Try importin from GB if gb.Has(path) { deps, err := gb.Parse(path) if err != nil { return false, []*cfg.Dependency{}, err } return true, deps, nil } // Try importing from gom if gom.Has(path) { deps, err := gom.Parse(path) if err != nil { return false, []*cfg.Dependency{}, err } return true, deps, nil } // When none are found. return false, []*cfg.Dependency{}, nil } ================================================ FILE: mirrors/cfg.go ================================================ 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"` } ================================================ FILE: mirrors/mirrors.go ================================================ // 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 } ================================================ FILE: mirrors/mirrors_test.go ================================================ 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") } } ================================================ FILE: mkdocs.yml ================================================ site_name: Glide Documentation pages: - Home: index.md - Getting Started: getting-started.md - The glide.yaml File: glide.yaml.md - Versions and Ranges: versions.md - Lock file: glide.lock.md - Commands: commands.md - Resolving Imports: resolving-imports.md - Vendor Directories: vendor.md - Plugins: plugins.md - F.A.Q.: faq.md theme: readthedocs ================================================ FILE: msg/msg.go ================================================ package msg import ( "bufio" "fmt" "io" "os" "strings" "sync" "github.com/Masterminds/vcs" ) // Messenger provides the underlying implementation that displays output to // users. type Messenger struct { sync.Mutex // Quiet, if true, suppresses chatty levels, like Info. Quiet bool // IsDebugging, if true, shows Debug. IsDebugging bool // NoColor, if true, will not use color in the output. NoColor bool // Stdout is the location where this prints output. Stdout io.Writer // Stderr is the location where this prints logs. Stderr io.Writer // Stdin is the location where input is read. Stdin io.Reader // PanicOnDie if true Die() will panic instead of exiting. PanicOnDie bool // The default exit code to use when dyping ecode int // If an error was been sent. hasErrored bool } // NewMessenger creates a default Messenger to display output. func NewMessenger() *Messenger { m := &Messenger{ Quiet: false, IsDebugging: false, NoColor: false, Stdout: os.Stdout, Stderr: os.Stderr, Stdin: os.Stdin, PanicOnDie: false, ecode: 1, } return m } // Default contains a default Messenger used by package level functions var Default = NewMessenger() // Info logs information func (m *Messenger) Info(msg string, args ...interface{}) { if m.Quiet { return } prefix := m.Color(Green, "[INFO]\t") m.Msg(prefix+msg, args...) } // Info logs information using the Default Messenger func Info(msg string, args ...interface{}) { Default.Info(msg, args...) } // Debug logs debug information func (m *Messenger) Debug(msg string, args ...interface{}) { if m.Quiet || !m.IsDebugging { return } prefix := "[DEBUG]\t" m.Msg(prefix+msg, args...) } // Debug logs debug information using the Default Messenger func Debug(msg string, args ...interface{}) { Default.Debug(msg, args...) } // Warn logs a warning func (m *Messenger) Warn(msg string, args ...interface{}) { prefix := m.Color(Yellow, "[WARN]\t") m.Msg(prefix+msg, args...) } // Warn logs a warning using the Default Messenger func Warn(msg string, args ...interface{}) { Default.Warn(msg, args...) } // Err logs an error. func (m *Messenger) Err(msg string, args ...interface{}) { prefix := m.Color(Red, "[ERROR]\t") m.Msg(prefix+msg, args...) m.hasErrored = true } // Err logs anderror using the Default Messenger func Err(msg string, args ...interface{}) { Default.Err(msg, args...) } // Die prints an error message and immediately exits the application. // If PanicOnDie is set to true a panic will occur instead of os.Exit being // called. func (m *Messenger) Die(msg string, args ...interface{}) { m.Err(msg, args...) if m.PanicOnDie { panic("trapped a Die() call") } os.Exit(m.ecode) } // Die prints an error message and immediately exits the application using the // Default Messenger. If PanicOnDie is set to true a panic will occur instead of // os.Exit being called. func Die(msg string, args ...interface{}) { Default.Die(msg, args...) } // ExitCode sets the exit code used by Die. // // The default is 1. // // Returns the old error code. func (m *Messenger) ExitCode(e int) int { m.Lock() old := m.ecode m.ecode = e m.Unlock() return old } // ExitCode sets the exit code used by Die using the Default Messenger. // // The default is 1. // // Returns the old error code. func ExitCode(e int) int { return Default.ExitCode(e) } // Msg prints a message with optional arguments, that can be printed, of // varying types. func (m *Messenger) Msg(msg string, args ...interface{}) { // When operations in Glide are happening concurrently messaging needs to be // 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") { msg += "\n" } if len(args) == 0 { fmt.Fprint(m.Stderr, msg) } 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 // varying types using the Default Messenger. func Msg(msg string, args ...interface{}) { Default.Msg(msg, args...) } // Puts formats a message and then prints to Stdout. // // It does not prefix the message, does not color it, or otherwise decorate it. // // It does add a line feed. func (m *Messenger) Puts(msg string, args ...interface{}) { // When operations in Glide are happening concurrently messaging needs to be // locked to avoid displaying one message in the middle of another one. m.Lock() defer m.Unlock() fmt.Fprintf(m.Stdout, msg, args...) fmt.Fprintln(m.Stdout) } // Puts formats a message and then prints to Stdout using the Default Messenger. // // It does not prefix the message, does not color it, or otherwise decorate it. // // It does add a line feed. func Puts(msg string, args ...interface{}) { Default.Puts(msg, args...) } // Print prints exactly the string given. // // It prints to Stdout. func (m *Messenger) Print(msg string) { // When operations in Glide are happening concurrently messaging needs to be // locked to avoid displaying one message in the middle of another one. m.Lock() defer m.Unlock() fmt.Fprint(m.Stdout, msg) } // Print prints exactly the string given using the Default Messenger. // // It prints to Stdout. func Print(msg string) { Default.Print(msg) } // HasErrored returns if Error has been called. // // This is useful if you want to known if Error was called to exit with a // non-zero exit code. func (m *Messenger) HasErrored() bool { return m.hasErrored } // HasErrored returns if Error has been called on the Default Messenger. // // This is useful if you want to known if Error was called to exit with a // non-zero exit code. func HasErrored() bool { return Default.HasErrored() } // Color returns a string in a certain color if colors are enabled and // available on that platform. func Color(code, msg string) string { return Default.Color(code, msg) } // PromptUntil provides a prompt until one of the passed in strings has been // entered and return is hit. Note, the comparisons are case insensitive meaning // Y == y. The returned value is the one from the passed in options (same case). func (m *Messenger) PromptUntil(opts []string) (string, error) { reader := bufio.NewReader(os.Stdin) for { text, err := reader.ReadString('\n') if err != nil { return "", err } for _, c := range opts { if strings.EqualFold(c, strings.TrimSpace(text)) { return c, nil } } } } // PromptUntil provides a prompt until one of the passed in strings has been // entered and return is hit. Note, the comparisons are case insensitive meaning // Y == y. The returned value is the one from the passed in options (same case). // Uses the default setup. func PromptUntil(opts []string) (string, error) { return Default.PromptUntil(opts) } // PromptUntilYorN provides a prompt until the user chooses yes or no. This is // not case sensitive and they can input other options such as Y or N. // In the response Yes is bool true and No is bool false. func (m *Messenger) PromptUntilYorN() bool { res, err := m.PromptUntil([]string{"y", "yes", "n", "no"}) if err != nil { m.Die("Error processing response: %s", err) } if res == "y" || res == "yes" { return true } return false } // PromptUntilYorN provides a prompt until the user chooses yes or no. This is // not case sensitive and they can input other options such as Y or N. // In the response Yes is bool true and No is bool false. // Uses the default setup. func PromptUntilYorN() bool { return Default.PromptUntilYorN() } ================================================ FILE: msg/out.go ================================================ // +build !windows package msg import "fmt" // These contanstants map to color codes for shell scripts making them // human readable. const ( Blue = "0;34" Red = "0;31" Green = "0;32" Yellow = "0;33" Cyan = "0;36" Pink = "1;35" ) // Color returns a string in a certain color. The first argument is a string // containing the color code or a constant from the table above mapped to a code. // // The following will print the string "Foo" in yellow: // fmt.Print(Color(Yellow, "Foo")) func (m *Messenger) Color(code, msg string) string { if m.NoColor { return msg } return fmt.Sprintf("\033[%sm%s\033[m", code, msg) } ================================================ FILE: msg/out_windows.go ================================================ // +build windows package msg // The color codes here are for compatibility with how Colors are used. Windows // colors have not been implemented yet. See https://github.com/Masterminds/glide/issues/158 // for more detail. const ( Blue = "" Red = "" Green = "" Yellow = "" Cyan = "" Pink = "" ) // Color on windows returns no color. See // https://github.com/Masterminds/glide/issues/158 if you want to help. func (m *Messenger) Color(code, msg string) string { return msg } ================================================ FILE: path/path.go ================================================ // Package path contains path and environment utilities for Glide. // // This includes tools to find and manipulate Go path variables, as well as // tools for copying from one path to another. package path import ( "fmt" "io" "os" "os/exec" "path/filepath" "strings" "github.com/mitchellh/go-homedir" ) // DefaultGlideFile is the default name for the glide.yaml file. const DefaultGlideFile = "glide.yaml" // VendorDir is the name of the directory that holds vendored dependencies. // // 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 = "" // GlideFile is the name of the Glide file. // // Setting this is not concurrency safe. For consistency, it should really // only be set once, at startup, or not at all. var GlideFile = DefaultGlideFile // LockFile is the default name for the lock file. const LockFile = "glide.lock" func init() { // As of Go 1.8 the GOPATH is no longer required to be set. Instead there // is a default value. If there is no GOPATH check for the default value. // Note, checking the GOPATH first to avoid invoking the go toolchain if // possible. if gopaths = os.Getenv("GOPATH"); len(gopaths) == 0 { goExecutable := os.Getenv("GLIDE_GO_EXECUTABLE") if len(goExecutable) <= 0 { goExecutable = "go" } out, err := exec.Command(goExecutable, "env", "GOPATH").Output() if err == nil { gopaths = strings.TrimSpace(string(out)) } } } // Home returns the Glide home directory ($GLIDE_HOME or ~/.glide, typically). // // This normalizes to an absolute path, and passes through os.ExpandEnv. func Home() string { if homeDir != "" { return homeDir } if h, err := homedir.Dir(); err == nil { homeDir = filepath.Join(h, ".glide") } else { cwd, err := os.Getwd() if err == nil { homeDir = filepath.Join(cwd, ".glide") } else { homeDir = ".glide" } } return homeDir } // SetHome sets the home directory for Glide. func SetHome(h string) { homeDir = h } // Vendor calculates the path to the vendor directory. // // Based on working directory, VendorDir and GlideFile, this attempts to // guess the location of the vendor directory. func Vendor() (string, error) { cwd, err := os.Getwd() if err != nil { return "", err } // Find the directory that contains glide.yaml yamldir, err := GlideWD(cwd) if err != nil { return cwd, err } 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 } // Glide gets the path to the closest glide file. func Glide() (string, error) { cwd, err := os.Getwd() if err != nil { return "", err } // Find the directory that contains glide.yaml yamldir, err := GlideWD(cwd) if err != nil { return cwd, err } gf := filepath.Join(yamldir, GlideFile) return gf, nil } // GlideWD finds the working directory of the glide.yaml file, starting at dir. // // If the glide file is not found in the current directory, it recurses up // a directory. func GlideWD(dir string) (string, error) { fullpath := filepath.Join(dir, GlideFile) if _, err := os.Stat(fullpath); err == nil { return dir, nil } base := filepath.Dir(dir) if base == dir { return "", fmt.Errorf("Cannot resolve parent of %s", base) } return GlideWD(base) } // Stores the gopaths so they do not get repeatedly looked up. This is especially // true when the default value needs to be retrieved from `go env GOPATH`. // TODO(mattfarina): Instead of a singleton an application context would be a // better place to store things like this. var gopaths string // Gopath gets GOPATH from environment and return the most relevant path. // // A GOPATH can contain a colon-separated list of paths. This retrieves the // GOPATH and returns only the FIRST ("most relevant") path. // // This should be used carefully. If, for example, you are looking for a package, // you may be better off using Gopaths. func Gopath() string { gopaths := Gopaths() if len(gopaths) == 0 { return "" } return gopaths[0] } // Gopaths retrieves the Gopath as a list when there is more than one path // listed in the Gopath. func Gopaths() []string { p := strings.Trim(gopaths, string(filepath.ListSeparator)) return filepath.SplitList(p) } // Basepath returns the current working directory. // // If there is an error getting the working directory, this returns ".", which // should function in cases where the directory is unlinked... Then again, // maybe not. func Basepath() string { base, err := os.Getwd() if err != nil { return "." } return base } // StripBasepath removes the base directory from a passed in path. func StripBasepath(p string) string { bp := Basepath() return strings.TrimPrefix(p, bp+string(os.PathSeparator)) } // IsLink returns true if the given FileInfo references a link. func IsLink(fi os.FileInfo) bool { return fi.Mode()&os.ModeSymlink == os.ModeSymlink } // HasLock returns true if this can stat a lockfile at the givin location. func HasLock(basepath string) bool { _, err := os.Stat(filepath.Join(basepath, LockFile)) return err == nil } // IsDirectoryEmpty checks if a directory is empty. func IsDirectoryEmpty(dir string) (bool, error) { f, err := os.Open(dir) if err != nil { return false, err } defer f.Close() _, err = f.Readdir(1) if err == io.EOF { return true, nil } return false, err } // CopyDir copies an entire source directory to the dest directory. // // This is akin to `cp -a src/* dest/` // // We copy the directory here rather than jumping out to a shell so we can // support multiple operating systems. func CopyDir(source string, dest string) error { // get properties of source dir si, err := os.Stat(source) if err != nil { return err } err = os.MkdirAll(dest, si.Mode()) if err != nil { return err } d, err := os.Open(source) if err != nil { return err } defer d.Close() objects, err := d.Readdir(-1) for _, obj := range objects { sp := filepath.Join(source, "/", obj.Name()) dp := filepath.Join(dest, "/", obj.Name()) if obj.IsDir() { err = CopyDir(sp, dp) if err != nil { return err } } else { // perform copy err = CopyFile(sp, dp) if err != nil { return err } } } return nil } // CopyFile copies a source file to a destination. // // It follows symbolic links and retains modes. func CopyFile(source string, dest string) error { ln, err := os.Readlink(source) if err == nil { return os.Symlink(ln, dest) } s, err := os.Open(source) if err != nil { return err } defer s.Close() d, err := os.Create(dest) if err != nil { return err } defer d.Close() _, err = io.Copy(d, s) if err != nil { return err } si, err := os.Stat(source) if err != nil { return err } err = os.Chmod(dest, si.Mode()) return err } ================================================ FILE: path/path_test.go ================================================ package path import ( "os" "path/filepath" "runtime" "testing" ) const testdata = "../testdata/path" func TestGlideWD(t *testing.T) { wd := filepath.Join(testdata, "a/b/c") found, err := GlideWD(wd) if err != nil { t.Errorf("Failed to get Glide directory: %s", err) } if found != filepath.Join(testdata, "a") { t.Errorf("Expected %s to match %s", found, filepath.Join(wd, "a")) } // This should fail wd = "/No/Such/Dir" found, err = GlideWD(wd) if err == nil { t.Errorf("Expected to get an error on a non-existent directory, not %s", found) } } func TestVendor(t *testing.T) { td, err := filepath.Abs(testdata) if err != nil { t.Fatal(err) } wd, _ := os.Getwd() os.Chdir(filepath.Join(td, "a", "b", "c")) res, err := Vendor() if err != nil { t.Errorf("Failed to resolve vendor directory: %s", err) } expect := filepath.Join(td, "a", "vendor") 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) } // Windows symlinks are different than *nix and they can be inconsistent. // The current testing only works for *nix testing and windows doesn't follow // the symlinks. If this is a vendor.lnk file in windows this won't work for // the go toolchain. If this is a windows link you need access to create one // which isn't consistent. // If there is a better way would love to know. if runtime.GOOS == "windows" { expect = filepath.Join(td, "x", "vendor") } else { 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) { wd, _ := os.Getwd() td, err := filepath.Abs(testdata) if err != nil { t.Fatal(err) } os.Chdir(filepath.Join(td, "a/b/c")) res, err := Glide() if err != nil { t.Errorf("Failed to resolve vendor directory: %s", err) } expect := filepath.Join(td, "a", "glide.yaml") if res != expect { t.Errorf("Failed to find vendor: expected %s got %s", expect, res) } os.Chdir(wd) } func TestCustomRemoveAll(t *testing.T) { td, err := filepath.Abs(testdata) if err != nil { t.Fatal(err) } // test that deleting a non-existent directory does not throw an error err = CustomRemoveAll(filepath.Join(td, "directory/doesnt/exist")) if err != nil { t.Errorf("Failed when removing non-existent directory %s", err) } // test that deleting a path with spaces does not throw an error spaceyPath := filepath.Join(td, "10942384 12341234 12343214 324134132323") err = os.MkdirAll(spaceyPath, 0777) if err != nil { t.Fatalf("Failed to make test directory %s", err) } err = CustomRemoveAll(spaceyPath) if err != nil { t.Errorf("Errored incorrectly when deleting a path with spaces %s", err) } if _, err = os.Stat(spaceyPath); !os.IsNotExist(err) { t.Errorf("Failed to successfully delete a path with spaces") } } ================================================ FILE: path/strip.go ================================================ package path import ( "os" "path/filepath" "github.com/Masterminds/glide/godep/strip" "github.com/Masterminds/glide/msg" ) func getWalkFunction(searchPath string, removeAll func(p string) error) func(path string, info os.FileInfo, err error) error { return func(path string, info os.FileInfo, err error) error { if err != nil { return err } // Skip the base vendor directory if path == searchPath { return nil } if info.Name() == "vendor" && info.IsDir() { msg.Info("Removing: %s", path) err = removeAll(path) if nil != err { return err } return filepath.SkipDir } return nil } } // StripVendor removes nested vendor and Godeps/_workspace/ directories. func StripVendor() error { searchPath, _ := Vendor() if _, err := os.Stat(searchPath); err != nil { if os.IsNotExist(err) { msg.Debug("Vendor directory does not exist.") } return err } err := filepath.Walk(searchPath, getWalkFunction(searchPath, CustomRemoveAll)) if err != nil { return err } return strip.GodepWorkspace(searchPath) } ================================================ FILE: path/strip_int_test.go ================================================ package path import ( "io/ioutil" "os" "path" "testing" ) func generateTestDirectory(t *testing.T) string { baseDir, err := ioutil.TempDir(os.TempDir(), "mgt") if nil != err { t.Error("Unable to create temp directory: ", err.Error()) } paths := map[string][]string{ "github.com/fake/log": {"log.go"}, "github.com/phoney/foo": {"bar.go"}, "github.com/phoney/foo/vendor": {"test.go", "foo.bar"}, "github.com/aws/aws-sdk-go/awsmigrate/awsmigrate-renamer/vendor": {}, "github.com/aws/aws-sdk-go/awsmigrate/awsmigrate-renamer/vendor/golang.org/x/tools/go/buildutil": {"allpackages.go", "tags.go", "fakecontext.go"}, "github.com/aws/aws-sdk-go/vendor": {"key_test.go", "key.go"}, "github.com/aws/aws-sdk-go/vendor/github.com/go-ini/ini": {"struct_test.go", "error.go", "ini_test.go"}, } os.OpenFile(path.Join(baseDir, "glide.yaml"), os.O_RDONLY|os.O_CREATE, 0666) for p, files := range paths { p = path.Join(baseDir, "vendor", p) if err = os.MkdirAll(p, 0777); nil != err { t.Errorf("Unable to create vendor dir: %s\n%s", p, err.Error()) } for _, f := range files { os.OpenFile(path.Join(p, f), os.O_RDONLY|os.O_CREATE, 0666) } } return baseDir } func TestNestVendorNoError(t *testing.T) { workingDir := generateTestDirectory(t) os.Chdir(workingDir) err := StripVendor() if nil != err { t.Errorf("Unexpected error in StripVendor: %s", err.Error()) } } ================================================ FILE: path/strip_test.go ================================================ package path import ( "errors" "os" "path/filepath" "reflect" "testing" "time" ) type mockFileInfo struct { name string isDir bool } func (mfi *mockFileInfo) Name() string { return mfi.name } func (mfi *mockFileInfo) Size() int64 { panic("not implemented") } func (mfi *mockFileInfo) Mode() os.FileMode { panic("not implemented") } func (mfi *mockFileInfo) ModTime() time.Time { panic("not implemented") } func (mfi *mockFileInfo) IsDir() bool { return mfi.isDir } func (mfi *mockFileInfo) Sys() interface{} { panic("not implemented") } type removeAll struct { calledWith string err error } func (rah *removeAll) removeAll(p string) error { rah.calledWith = p return rah.err } func TestWalkFunction(t *testing.T) { type args struct { searchPath string removeAll *removeAll path string info os.FileInfo err error } tests := []struct { name string args args want error wantCalledWith string }{ { name: "WalkFunctionSkipsNonVendor", args: args{searchPath: "foo", removeAll: &removeAll{}, path: "foo/bar", info: &mockFileInfo{name: "bar", isDir: true}, err: nil, }, want: nil, wantCalledWith: "", }, { name: "WalkFunctionSkipsNonDir", args: args{searchPath: "foo", removeAll: &removeAll{}, path: "foo/vendor", info: &mockFileInfo{name: "vendor", isDir: false}, err: nil, }, want: nil, wantCalledWith: "", }, { name: "WalkFunctionDeletesVendor", args: args{searchPath: "foo", removeAll: &removeAll{}, path: "foo/vendor", info: &mockFileInfo{name: "vendor", isDir: true}, err: nil, }, want: filepath.SkipDir, wantCalledWith: "foo/vendor", }, { name: "WalkFunctionReturnsPassedError", args: args{searchPath: "foo", removeAll: &removeAll{}, path: "foo/vendor", info: &mockFileInfo{name: "vendor", isDir: true}, err: errors.New("expected"), }, want: errors.New("expected"), wantCalledWith: "", }, { name: "WalkFunctionReturnsRemoveAllError", args: args{searchPath: "foo", removeAll: &removeAll{err: errors.New("expected")}, path: "foo/vendor", info: &mockFileInfo{name: "vendor", isDir: true}, err: nil, }, want: errors.New("expected"), wantCalledWith: "foo/vendor", }, { name: "WalkFunctionSkipsBaseDir", args: args{searchPath: "vendor", removeAll: &removeAll{}, path: "vendor", info: &mockFileInfo{name: "vendor", isDir: true}, err: nil, }, want: nil, wantCalledWith: "", }, } for _, test := range tests { walkFunction := getWalkFunction(test.args.searchPath, test.args.removeAll.removeAll) if actual := walkFunction(test.args.path, test.args.info, test.args.err); !reflect.DeepEqual(actual, test.want) { t.Errorf("walkFunction() = %v, want %v", actual, test.want) } if test.args.removeAll.calledWith != test.wantCalledWith { t.Errorf("removeAll argument = \"%s\", want \"%s\"", test.args.removeAll.calledWith, test.wantCalledWith) } } } ================================================ FILE: path/winbug.go ================================================ package path import ( "fmt" "os" "os/exec" "runtime" "syscall" "bytes" "io/ioutil" "github.com/Masterminds/glide/msg" ) // extract the exit code from an os.exec error func getExitCode(err error) int { if err != nil { if exitError, ok := err.(*exec.ExitError); ok { waitStatus := exitError.Sys().(syscall.WaitStatus) return waitStatus.ExitStatus() } } return 0 } // Hard to track down these codes - they are from windows.h and documented here: // https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx const ( winErrorFileNotFound = 2 winErrorPathNotFound = 3 ) // This file and its contents are to handle a Windows bug where large sets of // files fail when using the `os` package. This has been seen in Windows 10 // including the Windows Linux Subsystem. // Tracking the issue in https://github.com/golang/go/issues/20841. Once the // upstream issue is fixed this change can be reverted. // CustomRemoveAll is similar to os.RemoveAll but deals with the bug outlined // at https://github.com/golang/go/issues/20841. func CustomRemoveAll(p string) error { // Handle the windows case first if runtime.GOOS == "windows" { msg.Debug("Detected Windows. Removing files using windows command") cmd := exec.Command("cmd.exe", "/c", "rd", "/s", "/q", p) output, err := cmd.CombinedOutput() if err != nil { exitCode := getExitCode(err) if exitCode != winErrorFileNotFound && exitCode != winErrorPathNotFound { return fmt.Errorf("Error removing files: %s. output: %s", err, output) } } return nil } else if detectWsl() { cmd := exec.Command("rm", "-rf", p) output, err2 := cmd.CombinedOutput() msg.Debug("Detected Windows Subsystem for Linux. Removing files using subsystem command") if err2 != nil { return fmt.Errorf("Error removing files: %s. output: %s", err2, output) } return nil } return os.RemoveAll(p) } // CustomRename is similar to os.Rename but deals with the bug outlined // at https://github.com/golang/go/issues/20841. func CustomRename(o, n string) error { // Handking windows cases first if runtime.GOOS == "windows" { msg.Debug("Detected Windows. Moving files using windows command") cmd := exec.Command("cmd.exe", "/c", "move", o, n) output, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("Error moving files: %s. output: %s", err, output) } return nil } else if detectWsl() { cmd := exec.Command("mv", o, n) output, err2 := cmd.CombinedOutput() msg.Debug("Detected Windows Subsystem for Linux. Removing files using subsystem command") if err2 != nil { return fmt.Errorf("Error moving files: %s. output: %s", err2, output) } return nil } return os.Rename(o, n) } var procIsWin bool var procDet bool func detectWsl() bool { if !procDet { procDet = true _, err := os.Stat("/proc/version") if err == nil { b, err := ioutil.ReadFile("/proc/version") if err != nil { msg.Warn("Unable to read /proc/version that was detected. May incorrectly detect WSL") msg.Debug("Windows Subsystem for Linux detection error: %s", err) return false } if bytes.Contains(b, []byte("Microsoft")) { msg.Debug("Windows Subsystem for Linux detected") procIsWin = true } } } return procIsWin } ================================================ FILE: repo/installer.go ================================================ package repo import ( "fmt" "io/ioutil" "os" "path/filepath" "runtime" "strings" "sync" "syscall" "time" "github.com/Masterminds/glide/cache" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/dependency" "github.com/Masterminds/glide/importer" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/glide/util" "github.com/Masterminds/semver" "github.com/Masterminds/vcs" "github.com/codegangsta/cli" ) // Installer provides facilities for installing the repos in a config file. type Installer struct { // Force the install when certain normally stopping conditions occur. Force bool // Home is the location of cache Home string // Vendor contains the path to put the vendor packages Vendor string // ResolveAllFiles enables a resolver that will examine the dependencies // of every file of every package, rather than only following imported // packages. ResolveAllFiles bool // ResolveTest sets if test dependencies should be resolved. ResolveTest bool // Updated tracks the packages that have been remotely fetched. Updated *UpdateTracker } // NewInstaller returns an Installer instance ready to use. This is the constructor. func NewInstaller() *Installer { i := &Installer{} i.Updated = NewUpdateTracker() return i } // VendorPath returns the path to the location to put vendor packages func (i *Installer) VendorPath() string { if i.Vendor != "" { return i.Vendor } vp, err := gpath.Vendor() if err != nil { return filepath.FromSlash("./vendor") } return vp } // Install installs the dependencies from a Lockfile. func (i *Installer) Install(lock *cfg.Lockfile, conf *cfg.Config) (*cfg.Config, error) { // Create a config setup based on the Lockfile data to process with // existing commands. newConf := &cfg.Config{} newConf.Name = conf.Name newConf.Imports = make(cfg.Dependencies, len(lock.Imports)) for k, v := range lock.Imports { newConf.Imports[k] = cfg.DependencyFromLock(v) } newConf.DevImports = make(cfg.Dependencies, len(lock.DevImports)) for k, v := range lock.DevImports { newConf.DevImports[k] = cfg.DependencyFromLock(v) } newConf.DeDupe() 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...") 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. // // This is used when initializing an empty vendor directory, or when updating a // vendor directory based on changed config. func (i *Installer) Checkout(conf *cfg.Config) error { msg.Info("Downloading dependencies. Please wait...") if err := ConcurrentUpdate(conf.Imports, i, conf); err != nil { return err } if i.ResolveTest { return ConcurrentUpdate(conf.DevImports, i, conf) } return nil } // Update updates all dependencies. // // It begins with the dependencies in the config file, but also resolves // transitive dependencies. The returned lockfile has all of the dependencies // listed, but the version reconciliation has not been done. // // In other words, all versions in the Lockfile will be empty. func (i *Installer) Update(conf *cfg.Config) error { base := "." ic := newImportCache() m := &MissingPackageHandler{ home: i.Home, force: i.Force, Config: conf, Use: ic, updated: i.Updated, } v := &VersionHandler{ Use: ic, Imported: make(map[string]bool), Conflicts: make(map[string]bool), Config: conf, } // Update imports res, err := dependency.NewResolver(base) res.ResolveTest = i.ResolveTest if err != nil { msg.Die("Failed to create a resolver: %s", err) } res.Config = conf res.Handler = m res.VersionHandler = v res.ResolveAllFiles = i.ResolveAllFiles msg.Info("Resolving imports") imps, timps, err := res.ResolveLocal(false) if err != nil { msg.Die("Failed to resolve local packages: %s", err) } var deps cfg.Dependencies var tdeps cfg.Dependencies for _, v := range imps { n := res.Stripv(v) if conf.HasIgnore(n) { continue } rt, sub := util.NormalizeName(n) if sub == "" { sub = "." } d := deps.Get(rt) if d == nil { nd := &cfg.Dependency{ Name: rt, Subpackages: []string{sub}, } deps = append(deps, nd) } else if !d.HasSubpackage(sub) { d.Subpackages = append(d.Subpackages, sub) } } if i.ResolveTest { for _, v := range timps { n := res.Stripv(v) if conf.HasIgnore(n) { continue } rt, sub := util.NormalizeName(n) if sub == "" { sub = "." } d := deps.Get(rt) if d == nil { d = tdeps.Get(rt) } if d == nil { nd := &cfg.Dependency{ Name: rt, Subpackages: []string{sub}, } tdeps = append(tdeps, nd) } else if !d.HasSubpackage(sub) { d.Subpackages = append(d.Subpackages, sub) } } } _, err = allPackages(deps, res, false) if err != nil { msg.Die("Failed to retrieve a list of dependencies: %s", err) } if i.ResolveTest { msg.Debug("Resolving test dependencies") _, err = allPackages(tdeps, res, true) if err != nil { msg.Die("Failed to retrieve a list of test dependencies: %s", err) } } msg.Info("Downloading dependencies. Please wait...") err = ConcurrentUpdate(conf.Imports, i, conf) if err != nil { return err } if i.ResolveTest { err = ConcurrentUpdate(conf.DevImports, i, conf) if err != nil { return err } } 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") // Check if a .git directory exists under the old vendor dir. If it does, // move it over to the newly-generated vendor dir - the user is probably // submoduling, and it's easy enough not to break their setup. ivg := filepath.Join(i.VendorPath(), ".git") gitInfo, err := os.Stat(ivg) if err == nil { msg.Info("Preserving existing vendor/.git") vpg := filepath.Join(vp, ".git") err = os.Rename(ivg, vpg) if terr, ok := err.(*os.LinkError); ok { if gitInfo.IsDir() { err = fixcle(ivg, vpg, terr) } else { // When this is a submodule, .git is just a file. Don't try to copy // it as a directory in this case (see #828). err = gpath.CopyFile(ivg, vpg) } if err != nil { msg.Warn("Failed to preserve existing vendor/.git") } } } err = gpath.CustomRemoveAll(i.VendorPath()) if err != nil { return err } err = gpath.CustomRename(vp, i.VendorPath()) if terr, ok := err.(*os.LinkError); ok { return fixcle(vp, i.VendorPath(), terr) } return err } // fixcle is a helper function that tries to recover from cross-device rename // errors by falling back to copying. func fixcle(from, to string, terr *os.LinkError) error { // When there are different physical devices we cannot rename cross device. // Instead we copy. // syscall.EXDEV is the common name for the cross device link error // which has varying output text across different operating systems. if terr.Err == syscall.EXDEV { msg.Debug("Cross link err, trying manual copy: %s", terr) return gpath.CopyDir(from, to) } else if runtime.GOOS == "windows" { // In windows it can drop down to an operating system call that // returns an operating system error with a different number and // message. Checking for that as a fall back. noerr, ok := terr.Err.(syscall.Errno) // 0x11 (ERROR_NOT_SAME_DEVICE) is the windows error. // See https://msdn.microsoft.com/en-us/library/cc231199.aspx if ok && noerr == 0x11 { msg.Debug("Cross link err on Windows, trying manual copy: %s", terr) return gpath.CopyDir(from, to) } } return terr } // List resolves the complete dependency tree and returns a list of dependencies. func (i *Installer) List(conf *cfg.Config) []*cfg.Dependency { base := "." ic := newImportCache() v := &VersionHandler{ Use: ic, Imported: make(map[string]bool), Conflicts: make(map[string]bool), Config: conf, } // Update imports res, err := dependency.NewResolver(base) if err != nil { msg.Die("Failed to create a resolver: %s", err) } res.Config = conf res.VersionHandler = v res.ResolveAllFiles = i.ResolveAllFiles msg.Info("Resolving imports") _, _, err = res.ResolveLocal(false) if err != nil { msg.Die("Failed to resolve local packages: %s", err) } _, err = allPackages(conf.Imports, res, false) if err != nil { msg.Die("Failed to retrieve a list of dependencies: %s", err) } if len(conf.DevImports) > 0 { msg.Warn("dev imports not resolved.") } return conf.Imports } // 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, i *Installer, c *cfg.Config) error { newDeps := []*cfg.Dependency{} for _, dep := range deps { 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) if err != nil { newDeps = append(newDeps, dep) continue } ver, err := repo.Version() if err != nil { newDeps = append(newDeps, dep) 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, i, c) } return nil } // ConcurrentUpdate takes a list of dependencies and updates in parallel. 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 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) 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. 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 deps { if !c.HasIgnore(dep.Name) { wg.Add(1) in <- dep } } wg.Wait() // Close goroutines setting the version for ii := 0; ii < concurrentWorkers; ii++ { done <- struct{}{} } return returnErr } // allPackages gets a list of all packages required to satisfy the given deps. func allPackages(deps []*cfg.Dependency, res *dependency.Resolver, addTest bool) ([]string, error) { if len(deps) == 0 { return []string{}, nil } vdir, err := gpath.Vendor() if err != nil { return []string{}, err } vdir += string(os.PathSeparator) ll, err := res.ResolveAll(deps, addTest) if err != nil { return []string{}, err } for i := 0; i < len(ll); i++ { ll[i] = strings.TrimPrefix(ll[i], vdir) } return ll, nil } // MissingPackageHandler is a dependency.MissingPackageHandler. // // When a package is not found, this attempts to resolve and fetch. // // When a package is found on the GOPATH, this notifies the user. type MissingPackageHandler struct { home string force bool Config *cfg.Config Use *importCache updated *UpdateTracker } // 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) { err := m.fetchToCache(pkg, addTest) if err != nil { return false, err } 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) { err := m.fetchToCache(pkg, addTest) if err != nil { return false, err } 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) // For the parent applications source skip the cache. if root == m.Config.Name { pth := gpath.Basepath() return filepath.Join(pth, filepath.FromSlash(sub)) } 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, filepath.FromSlash(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 } 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) } } return VcsUpdate(d, m.force, m.updated) } // VersionHandler handles setting the proper version in the VCS. type VersionHandler struct { // If Try to use the version here if we have one. This is a cache and will // change over the course of setting versions. Use *importCache // Cache if importing scan has already occurred here. Imported map[string]bool Config *cfg.Config // There's a problem where many sub-packages have been asked to set a version // and you can end up with numerous conflict messages that are exactly the // same. We are keeping track to only display them once. // the parent pac Conflicts map[string]bool } // Process imports dependencies for a package func (d *VersionHandler) Process(pkg string) (e error) { root := util.GetRootFromPackage(pkg) // Skip any references to the root package. if root == d.Config.Name { return nil } // We have not tried to import, yet. // Should we look in places other than the root of the project? if d.Imported[root] == false { d.Imported[root] = true p := d.pkgPath(root) f, deps, err := importer.Import(p) if f && err == nil { for _, dep := range deps { // The fist one wins. Would something smater than this be better? exists, _ := d.Use.Get(dep.Name) if exists == nil && (dep.Reference != "" || dep.Repository != "") { d.Use.Add(dep.Name, dep, root) } } } else if err != nil { msg.Err("Unable to import from %s. Err: %s", root, err) e = err } } return } // SetVersion sets the version for a package. If that package version is already // set it handles the case by: // - keeping the already set version // - proviting messaging about the version conflict // TODO(mattfarina): The way version setting happens can be improved. Currently not optimal. func (d *VersionHandler) SetVersion(pkg string, addTest bool) (e error) { root := util.GetRootFromPackage(pkg) // Skip any references to the root package. if root == d.Config.Name { return nil } v := d.Config.Imports.Get(root) if addTest { if v == nil { v = d.Config.DevImports.Get(root) } else if d.Config.DevImports.Has(root) { // Both imports and test imports lists the same dependency. // There are import chains (because the import tree is resolved // before the test tree) that can cause this. tempD := d.Config.DevImports.Get(root) if tempD.Reference != v.Reference { msg.Warn("Using import %s (version %s) for test instead of testImport (version %s).", v.Name, v.Reference, tempD.Reference) } // TODO(mattfarina): Note repo difference in a warning. } } dep, req := d.Use.Get(root) if dep != nil && v != nil { if v.Reference == "" && dep.Reference != "" { v.Reference = dep.Reference // Clear the pin, if set, so the new version can be used. v.Pin = "" dep = v } else if v.Reference != "" && dep.Reference != "" && v.Reference != dep.Reference { dest := d.pkgPath(pkg) dep = determineDependency(v, dep, dest, req) } else { dep = v } } else if v != nil { dep = v } else if dep != nil { // We've got an imported dependency to use and don't already have a // record of it. Append it to the Imports. if addTest { d.Config.DevImports = append(d.Config.DevImports, dep) } else { d.Config.Imports = append(d.Config.Imports, dep) } } else { // If we've gotten here we don't have any depenency objects. r, sp := util.NormalizeName(pkg) dep = &cfg.Dependency{ Name: r, } if sp != "" { dep.Subpackages = []string{sp} } if addTest { d.Config.DevImports = append(d.Config.DevImports, dep) } else { d.Config.Imports = append(d.Config.Imports, dep) } } err := VcsVersion(dep) if err != nil { msg.Warn("Unable to set version on %s to %s. Err: %s", root, dep.Reference, err) e = err } return } func (d *VersionHandler) pkgPath(pkg string) string { root, sub := util.NormalizeName(pkg) // For the parent applications source skip the cache. if root == d.Config.Name { pth := gpath.Basepath() return filepath.Join(pth, filepath.FromSlash(sub)) } 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, filepath.FromSlash(sub)) } func determineDependency(v, dep *cfg.Dependency, dest, req string) *cfg.Dependency { repo, err := v.GetRepo(dest) if err != nil { singleWarn("Unable to access repo for %s\n", v.Name) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } vIsRef := repo.IsReference(v.Reference) depIsRef := repo.IsReference(dep.Reference) // Both are references and they are different ones. if vIsRef && depIsRef { singleWarn("Conflict: %s rev is currently %s, but %s wants %s\n", v.Name, v.Reference, req, dep.Reference) displayCommitInfo(repo, v) displayCommitInfo(repo, dep) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } else if vIsRef { // The current one is a reference and the suggestion is a SemVer constraint. con, err := semver.NewConstraint(dep.Reference) if err != nil { singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", dep.Name, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } ver, err := semver.NewVersion(v.Reference) if err != nil { // The existing version is not a semantic version. singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) displayCommitInfo(repo, v) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } if con.Check(ver) { singleInfo("Keeping %s %s because it fits constraint '%s'", v.Name, v.Reference, dep.Reference) return v } singleWarn("Conflict: %s version is %s but does not meet constraint '%s'\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } else if depIsRef { con, err := semver.NewConstraint(v.Reference) if err != nil { singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", v.Name, v.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } ver, err := semver.NewVersion(dep.Reference) if err != nil { singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) displayCommitInfo(repo, dep) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } if con.Check(ver) { v.Reference = dep.Reference singleInfo("Using %s %s because it fits constraint '%s'", v.Name, v.Reference, v.Reference) return v } singleWarn("Conflict: %s semantic version constraint is %s but '%s' does not meet the constraint\n", v.Name, v.Reference, v.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } // Neither is a vcs reference and both could be semantic version // constraints that are different. _, err = semver.NewConstraint(dep.Reference) if err != nil { // dd.Reference is not a reference or a valid constraint. singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", dep.Name, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } _, err = semver.NewConstraint(v.Reference) if err != nil { // existing.Reference is not a reference or a valid constraint. // We really should never end up here. singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", v.Name, v.Reference) v.Reference = dep.Reference v.Pin = "" singleInfo("Using %s %s because it is a valid version", v.Name, v.Reference) return v } // Both versions are constraints. Try to merge them. // If either comparison has an || skip merging. That's complicated. ddor := strings.Index(dep.Reference, "||") eor := strings.Index(v.Reference, "||") if ddor == -1 && eor == -1 { // Add the comparisons together. newRef := v.Reference + ", " + dep.Reference v.Reference = newRef v.Pin = "" singleInfo("Combining %s semantic version constraints %s and %s", v.Name, v.Reference, dep.Reference) return v } singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } var warningMessage = make(map[string]bool) var infoMessage = make(map[string]bool) func singleWarn(ft string, v ...interface{}) { m := fmt.Sprintf(ft, v...) _, f := warningMessage[m] if !f { msg.Warn(m) warningMessage[m] = true } } func singleInfo(ft string, v ...interface{}) { m := fmt.Sprintf(ft, v...) _, f := infoMessage[m] if !f { msg.Info(m) infoMessage[m] = true } } type importCache struct { cache map[string]*cfg.Dependency from map[string]string } func newImportCache() *importCache { return &importCache{ cache: make(map[string]*cfg.Dependency), from: make(map[string]string), } } func (i *importCache) Get(name string) (*cfg.Dependency, string) { d, f := i.cache[name] if f { return d, i.from[name] } return nil, "" } func (i *importCache) Add(name string, dep *cfg.Dependency, root string) { i.cache[name] = dep i.from[name] = root } var displayCommitInfoPrefix = msg.Default.Color(msg.Green, "[INFO] ") var displayCommitInfoTemplate = "%s reference %s:\n" + displayCommitInfoPrefix + "- author: %s\n" + displayCommitInfoPrefix + "- commit date: %s\n" + displayCommitInfoPrefix + "- subject (first line): %s\n" func displayCommitInfo(repo vcs.Repo, dep *cfg.Dependency) { c, err := repo.CommitInfo(dep.Reference) ref := dep.Reference if err == nil { tgs, err2 := repo.TagsFromCommit(c.Commit) if err2 == nil && len(tgs) > 0 { if tgs[0] != dep.Reference { ref = ref + " (" + tgs[0] + ")" } } singleInfo(displayCommitInfoTemplate, dep.Name, ref, c.Author, c.Date.Format(time.RFC1123Z), commitSubjectFirstLine(c.Message)) } } func commitSubjectFirstLine(sub string) string { lines := strings.Split(sub, "\n") if len(lines) <= 1 { return sub } return lines[0] } ================================================ FILE: repo/repo.go ================================================ // Package repo provides tools for working with VCS repositories. // // Glide manages repositories in the vendor directory by using the native VCS // systems of each repository upon which the code relies. package repo // concurrentWorkers is the number of workers to be used in concurrent operations. var concurrentWorkers = 20 // UpdatingVendored indicates whether this run of Glide is updating a vendored vendor/ path. // // It is related to the --update-vendor flag for update and install. // // TODO: This is legacy, and maybe we should handle it differently. It should // be set either 0 or 1 times, and only at startup. //var UpdatingVendored bool = false ================================================ FILE: repo/semver.go ================================================ package repo import ( "github.com/Masterminds/semver" "github.com/Masterminds/vcs" ) // Filter a list of versions to only included semantic versions. The response // is a mapping of the original version to the semantic version. func getSemVers(refs []string) []*semver.Version { sv := []*semver.Version{} for _, r := range refs { v, err := semver.NewVersion(r) if err == nil { sv = append(sv, v) } } return sv } // Get all the references for a repo. This includes the tags and branches. func getAllVcsRefs(repo vcs.Repo) ([]string, error) { tags, err := repo.Tags() if err != nil { return []string{}, err } branches, err := repo.Branches() if err != nil { return []string{}, err } return append(branches, tags...), nil } ================================================ FILE: repo/set_reference.go ================================================ package repo import ( "sync" "github.com/Masterminds/glide/cache" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" "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 { if len(conf.Imports) == 0 && len(conf.DevImports) == 0 { msg.Info("No references set.\n") return nil } 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: 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 } } }(in) } for _, dep := range conf.Imports { if !conf.HasIgnore(dep.Name) { wg.Add(1) in <- dep } } if resolveTest { for _, dep := range conf.DevImports { if !conf.HasIgnore(dep.Name) { wg.Add(1) in <- dep } } } wg.Wait() // Close goroutines setting the version for i := 0; i < concurrentWorkers; i++ { done <- struct{}{} } // close(done) // close(in) return returnErr } ================================================ FILE: repo/tracker.go ================================================ package repo import ( "sync" ) // UpdateTracker holds a list of all the packages that have been updated from // an external source. This is a concurrency safe implementation. type UpdateTracker struct { sync.RWMutex updated map[string]bool } // NewUpdateTracker creates a new instance of UpdateTracker ready for use. func NewUpdateTracker() *UpdateTracker { u := &UpdateTracker{} u.updated = map[string]bool{} return u } // Add adds a name to the list of items being tracked. func (u *UpdateTracker) Add(name string) { u.Lock() u.updated[name] = true u.Unlock() } // Check returns if an item is on the list or not. func (u *UpdateTracker) Check(name string) bool { u.RLock() _, f := u.updated[name] u.RUnlock() return f } // Remove takes a package off the list func (u *UpdateTracker) Remove(name string) { u.Lock() delete(u.updated, name) u.Unlock() } ================================================ FILE: repo/tracker_test.go ================================================ package repo import "testing" func TestUpdateTracker(t *testing.T) { tr := NewUpdateTracker() if f := tr.Check("github.com/foo/bar"); f != false { t.Error("Error, package Check passed on empty tracker") } tr.Add("github.com/foo/bar") if f := tr.Check("github.com/foo/bar"); f != true { t.Error("Error, failed to add package to tracker") } tr.Remove("github.com/foo/bar") if f := tr.Check("github.com/foo/bar"); f != false { t.Error("Error, failed to remove package from tracker") } } ================================================ FILE: repo/vcs.go ================================================ package repo import ( "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "os" "path/filepath" "runtime" "sort" "strings" cp "github.com/Masterminds/glide/cache" "github.com/Masterminds/glide/cfg" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/semver" v "github.com/Masterminds/vcs" ) // VcsUpdate updates to a particular checkout based on the VCS setting. 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. if dep.Pin != "" { msg.Debug("Dependency %s has already been pinned. Fetching updates skipped", dep.Name) return nil } if updated.Check(dep.Name) { msg.Debug("%s was already updated, skipping", dep.Name) return nil } updated.Add(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) { 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. empty, err := gpath.IsDirectoryEmpty(dest) if err != nil { return err } _, err = v.DetectVcsFromFS(dest) 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); err != nil { msg.Warn("Unable to checkout %s\n", dep.Name) return err } } else { repo, err := dep.GetRepo(dest) // Tried to checkout a repo to a path that does not work. Either the // type or endpoint has changed. Force is being passed in so the old // location can be removed and replaced with the new one. // 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 { 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); err != nil { msg.Warn("Unable to checkout %s\n", dep.Name) return err } repo, err = dep.GetRepo(dest) if err != nil { return err } } else if err != nil { return err } else if repo.IsDirty() { 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 ver != "" { version, err := repo.Version() if err != nil { return err } ib, err := isBranch(ver, repo) if err != nil { return err } // 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 == ver && !ib { msg.Debug("%s is already set to version %s. Skipping update", dep.Name, dep.Reference) return nil } } if err := repo.Update(); err != nil { msg.Warn("Download failed.\n") return err } } } return nil } // VcsVersion set the VCS version for a checkout. 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. if dep.Pin != "" { msg.Debug("Dependency %s has already been pinned. Setting version skipped", dep.Name) return nil } 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 == "" { // Before exiting update the pinned version repo, err := dep.GetRepo(cwd) if err != nil { return err } dep.Pin, err = repo.Version() if err != nil { return err } return nil } // When the directory is not empty and has no VCS directory it's // a vendored files situation. empty, err := gpath.IsDirectoryEmpty(cwd) if err != nil { return err } _, err = v.DetectVcsFromFS(cwd) if empty == false && err == v.ErrCannotDetectVCS { 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 { // 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) } } if err := repo.UpdateVersion(ver); err != nil { return err } dep.Pin, err = repo.Version() if err != nil { return err } return nil } // VcsGet figures out how to fetch a dependency, and then gets it. // // VcsGet installs into the cache. func VcsGet(dep *cfg.Dependency) error { 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) 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 != "" { 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 nil } // filterArchOs indicates a dependency should be filtered out because it is // the wrong GOOS or GOARCH. // // FIXME: Should this be moved to the dependency package? func filterArchOs(dep *cfg.Dependency) bool { found := false if len(dep.Arch) > 0 { for _, a := range dep.Arch { if a == runtime.GOARCH { found = true } } // If it's not found, it should be filtered out. if !found { return true } } found = false if len(dep.Os) > 0 { for _, o := range dep.Os { if o == runtime.GOOS { found = true } } if !found { return true } } return false } // isBranch returns true if the given string is a branch in VCS. func isBranch(branch string, repo v.Repo) (bool, error) { branches, err := repo.Branches() if err != nil { return false, err } for _, b := range branches { if b == branch { return true, nil } } return false, nil } // 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) string { // Svn and Bzr use different locations (paths or entire locations) // for branches so we won't have a default branch. if repo.Vcs() == v.Svn || repo.Vcs() == v.Bzr { return "" } // Check the cache for a value. key, kerr := cp.Key(repo.Remote()) var d cp.RepoInfo if kerr == nil { d, err := cp.RepoData(key) if err == nil { if d.DefaultBranch != "" { return d.DefaultBranch } } } // If we don't have it in the store try some APIs r := repo.Remote() u, err := url.Parse(r) if err != nil { return "" } if u.Scheme == "" { // Where there is no scheme we try urls like git@github.com:foo/bar r = strings.Replace(r, ":", "/", -1) r = "ssh://" + r u, err = url.Parse(r) if err != nil { return "" } u.Scheme = "" } if u.Host == "github.com" { parts := strings.Split(u.Path, "/") if len(parts) != 2 { return "" } api := fmt.Sprintf("https://api.github.com/repos/%s/%s", parts[0], parts[1]) resp, err := http.Get(api) if err != nil { return "" } defer resp.Body.Close() if resp.StatusCode >= 300 || resp.StatusCode < 200 { return "" } body, err := ioutil.ReadAll(resp.Body) var data interface{} err = json.Unmarshal(body, &data) if err != nil { return "" } gh := data.(map[string]interface{}) db := gh["default_branch"].(string) if kerr == nil { d.DefaultBranch = db err := cp.SaveRepoData(key, d) 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) } } return db } if u.Host == "bitbucket.org" { parts := strings.Split(u.Path, "/") if len(parts) != 2 { return "" } api := fmt.Sprintf("https://bitbucket.org/api/1.0/repositories/%s/%s/main-branch/", parts[0], parts[1]) resp, err := http.Get(api) if err != nil { return "" } defer resp.Body.Close() if resp.StatusCode >= 300 || resp.StatusCode < 200 { return "" } body, err := ioutil.ReadAll(resp.Body) var data interface{} err = json.Unmarshal(body, &data) if err != nil { return "" } bb := data.(map[string]interface{}) db := bb["name"].(string) if kerr == nil { d.DefaultBranch = db err := cp.SaveRepoData(key, d) 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) } } return db } return "" } // From a local repo find out the current branch name if there is one. // Note, this should only be used right after a fresh clone to get accurate // information. func findCurrentBranch(repo v.Repo) string { msg.Debug("Attempting to find current branch for %s", repo.Remote()) // Svn and Bzr don't have default branches. if repo.Vcs() == v.Svn || repo.Vcs() == v.Bzr { return "" } if repo.Vcs() == v.Git || repo.Vcs() == v.Hg { ver, err := repo.Current() if err != nil { msg.Debug("Unable to find current branch for %s, error: %s", repo.Remote(), err) return "" } return ver } return "" } func envForDir(dir string) []string { env := os.Environ() return mergeEnvLists([]string{"PWD=" + dir}, env) } func mergeEnvLists(in, out []string) []string { NextVar: for _, inkv := range in { k := strings.SplitAfterN(inkv, "=", 2)[0] for i, outkv := range out { if strings.HasPrefix(outkv, k) { out[i] = inkv continue NextVar } } out = append(out, inkv) } return out } ================================================ FILE: testdata/name/glide.yaml ================================================ package: technosophos.com/x/foo import: [] ================================================ FILE: testdata/name/glide2.yaml ================================================ package: another/name import: [] ================================================ FILE: testdata/nv/a/foo.empty ================================================ ================================================ FILE: testdata/nv/b/foo.empty ================================================ ================================================ FILE: testdata/nv/c/foo.empty ================================================ ================================================ FILE: testdata/path/a/b/c/placeholder.empty ================================================ ================================================ FILE: testdata/path/a/glide.yaml ================================================ ================================================ FILE: testdata/path/x/glide.yaml ================================================ ================================================ FILE: testdata/path/x/symlinked_vendor/placeholder.empty ================================================ ================================================ FILE: testdata/path/x/y/z/placeholder.empty ================================================ ================================================ FILE: testdata/plugin/glide-hello ================================================ #!/bin/bash echo "Hello from the other glide" ================================================ FILE: testdata/plugin/glide-hello-win.bat ================================================ @echo off echo "Hello from the other glide" ================================================ FILE: testdata/rebuild/glide.yaml ================================================ package: github.com/Masterminds/glide/testdata/plugin import: - package: example.com/x/foo ================================================ FILE: tree/tree.go ================================================ package tree import ( "container/list" "os" "path/filepath" "strings" "github.com/Masterminds/glide/dependency" "github.com/Masterminds/glide/msg" gpath "github.com/Masterminds/glide/path" "github.com/Masterminds/glide/util" ) // Display displays a tree view of the given project. // // FIXME: The output formatting could use some TLC. func Display(b *util.BuildCtxt, basedir, myName string, level int, core bool, l *list.List) { deps := walkDeps(b, basedir, myName) for _, name := range deps { found := findPkg(b, name, basedir) if found.Loc == dependency.LocUnknown { m := "glide get " + found.Name msg.Puts("\t%s\t(%s)", found.Name, m) continue } if !core && found.Loc == dependency.LocGoroot || found.Loc == dependency.LocCgo { continue } msg.Print(strings.Repeat("|\t", level-1) + "|-- ") f := findInList(found.Name, l) if f == true { msg.Puts("(Recursion) %s (%s)", found.Name, found.Path) } else { // Every branch in the tree is a copy to handle all the branches cl := copyList(l) cl.PushBack(found.Name) msg.Puts("%s (%s)", found.Name, found.Path) Display(b, found.Path, found.Name, level+1, core, cl) } } } 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 } return nil } var imps []string pkg, err := b.ImportDir(path, 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. imps, _, err = dependency.IterativeScan(path) if err != nil { msg.Err("Error walking dependencies for %s: %s", path, err) return err } } else if err != nil { if !strings.HasPrefix(err.Error(), "no buildable Go source") { msg.Warn("Error: %s (%s)", err, path) // Not sure if we should return here. //return err } } else { imps = pkg.Imports } if pkg.Goroot { return nil } for _, imp := range imps { //if strings.HasPrefix(imp, myName) { ////Info("Skipping %s because it is a subpackage of %s", imp, myName) //continue //} if imp == myName { continue } externalDeps = append(externalDeps, imp) } return nil }) return externalDeps } func findPkg(b *util.BuildCtxt, name, cwd string) *dependency.PkgInfo { var fi os.FileInfo var err error var p string info := &dependency.PkgInfo{ Name: name, } if strings.HasPrefix(name, "./") || strings.HasPrefix(name, "../") { info.Loc = dependency.LocRelative return info } // Recurse backward to scan other vendor/ directories // If the cwd isn't an absolute path walking upwards looking for vendor/ // folders can get into an infinate loop. abs, err := filepath.Abs(cwd) if err != nil { abs = cwd } if abs != "." { // Previously there was a check on the loop that wd := "/". The path // "/" is a POSIX path so this fails on Windows. Now the check is to // make sure the same wd isn't seen twice. When the same wd happens // more than once it's the beginning of looping on the same location // which is the top level. pwd := "" for wd := abs; wd != pwd; wd = filepath.Dir(wd) { pwd = wd // Don't look for packages outside the GOPATH // Note, the GOPATH may or may not end with the path separator. // The output of filepath.Dir does not the the path separator on the // end so we need to test both. if wd == b.GOPATH || wd+string(os.PathSeparator) == b.GOPATH { break } p = filepath.Join(wd, "vendor", name) if fi, err = os.Stat(p); err == nil && (fi.IsDir() || gpath.IsLink(fi)) { info.Path = p info.Loc = dependency.LocVendor info.Vendored = true return info } } } // Check $GOPATH for _, r := range strings.Split(b.GOPATH, ":") { p = filepath.Join(r, "src", name) if fi, err = os.Stat(p); err == nil && (fi.IsDir() || gpath.IsLink(fi)) { info.Path = p info.Loc = dependency.LocGopath return info } } // Check $GOROOT for _, r := range strings.Split(b.GOROOT, ":") { p = filepath.Join(r, "src", name) if fi, err = os.Stat(p); err == nil && (fi.IsDir() || gpath.IsLink(fi)) { info.Path = p info.Loc = dependency.LocGoroot return info } } // If this is "C", we're dealing with cgo if name == "C" { info.Loc = dependency.LocCgo } else if name == "appengine" || name == "appengine_internal" || strings.HasPrefix(name, "appengine/") || strings.HasPrefix(name, "appengine_internal/") { // Appengine is a special case when it comes to Go builds. It is a local // looking package only available within appengine. It's a special case // where Google products are playing with each other. // https://blog.golang.org/the-app-engine-sdk-and-workspaces-gopath info.Loc = dependency.LocAppengine } else if _, ok := dependency.PackagesAddedToStdlib[name]; ok { // Various packages are being added to the Go standard library, and being imported // with build flags. Need to detect this and handle it. info.Loc = dependency.LocGoroot } return info } // copyList copies an existing list to a new list. func copyList(l *list.List) *list.List { n := list.New() for e := l.Front(); e != nil; e = e.Next() { n.PushBack(e.Value.(string)) } return n } // findInList searches a list haystack for a string needle. func findInList(n string, l *list.List) bool { for e := l.Front(); e != nil; e = e.Next() { if e.Value.(string) == n { return true } } return false } ================================================ FILE: tree/tree_test.go ================================================ /* Package tree contains functions for printing a dependency tree. The future of the tree functionality is uncertain, as it is neither core to the functionality of Glide, nor particularly complementary. Its principal use case is for debugging the generated dependency tree. Currently, the tree package builds its dependency tree in a slightly different way than the `dependency` package does. This should not make any practical difference, though code-wise it would be nice to change this over to use the `dependency` resolver. */ package tree import ( "container/list" "testing" ) func TestFindInTree(t *testing.T) { l := list.New() l.PushBack("github.com/Masterminds/glide") l.PushBack("github.com/Masterminds/vcs") l.PushBack("github.com/Masterminds/semver") f := findInList("foo", l) if f != false { t.Error("findInList found true instead of false") } f = findInList("github.com/Masterminds/vcs", l) if f != true { t.Error("findInList found false instead of true") } } ================================================ FILE: util/normalizename_test.go ================================================ package util import ( "testing" ) func TestNormalizeName(t *testing.T) { packages := []struct { input string root string extra string }{ { input: "github.com/Masterminds/cookoo/web/io/foo", root: "github.com/Masterminds/cookoo", extra: "web/io/foo", }, { input: `github.com\Masterminds\cookoo\web\io\foo`, root: "github.com/Masterminds/cookoo", extra: "web/io/foo", }, { input: "golang.org/x/crypto/ssh", root: "golang.org/x/crypto", extra: "ssh", }, { input: "incomplete/example", root: "incomplete/example", 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 { t.Errorf("%s: Expected root '%s', got '%s'", test.input, test.root, root) } if extra != test.extra { t.Errorf("%s: Expected extra '%s', got '%s'", test.input, test.extra, extra) } } } ================================================ FILE: util/util.go ================================================ package util import ( "encoding/xml" "fmt" "go/build" "io" "net/http" "net/url" "os" "os/exec" "path/filepath" "regexp" "strings" "github.com/Masterminds/vcs" ) // ResolveCurrent selects whether the package should only the dependencies for // the current OS/ARCH instead of all possible permutations. // This is not concurrently safe which is ok for the current application. If // other needs arise it may need to be re-written. var ResolveCurrent = false // goRoot caches the GOROOT variable for build contexts. If $GOROOT is not set in // the user's environment, then the context's root path is 'go env GOROOT'. var goRoot string func init() { // Precompile the regular expressions used to check VCS locations. for _, v := range vcsList { v.regex = regexp.MustCompile(v.pattern) } if goRoot = os.Getenv("GOROOT"); len(goRoot) == 0 { goExecutable := os.Getenv("GLIDE_GO_EXECUTABLE") if len(goExecutable) <= 0 { goExecutable = "go" } out, err := exec.Command(goExecutable, "env", "GOROOT").Output() if err == nil { goRoot = strings.TrimSpace(string(out)) } } } func toSlash(v string) string { return strings.Replace(v, "\\", "/", -1) } // GetRootFromPackage retrives the top level package from a name. // // From a package name find the root repo. For example, // the package github.com/Masterminds/cookoo/io has a root repo // at github.com/Masterminds/cookoo func GetRootFromPackage(pkg string) string { pkg = toSlash(pkg) for _, v := range vcsList { m := v.regex.FindStringSubmatch(pkg) if m == nil { continue } if m[1] != "" { return m[1] } } // There are cases where a package uses the special go get magic for // redirects. If we've not discovered the location already try that. pkg = getRootFromGoGet(pkg) return pkg } // Pages like https://golang.org/x/net provide an html document with // meta tags containing a location to work with. The go tool uses // a meta tag with the name go-import which is what we use here. // godoc.org also has one call go-source that we do not need to use. // The value of go-import is in the form "prefix vcs repo". The prefix // should match the vcsURL and the repo is a location that can be // checked out. Note, to get the html document you you need to add // ?go-get=1 to the url. func getRootFromGoGet(pkg string) string { p, found := checkRemotePackageCache(pkg) if found { return p } vcsURL := "https://" + pkg u, err := url.Parse(vcsURL) if err != nil { return pkg } if u.RawQuery == "" { u.RawQuery = "go-get=1" } else { u.RawQuery = u.RawQuery + "&go-get=1" } checkURL := u.String() resp, err := http.Get(checkURL) if err != nil { addToRemotePackageCache(pkg, pkg) return pkg } defer resp.Body.Close() nu, err := parseImportFromBody(u, resp.Body) if err != nil { addToRemotePackageCache(pkg, pkg) return pkg } else if nu == "" { addToRemotePackageCache(pkg, pkg) return pkg } addToRemotePackageCache(pkg, nu) return nu } // The caching is not concurrency safe but should be made to be that way. // This implementation is far too much of a hack... rewrite needed. var remotePackageCache = make(map[string]string) func checkRemotePackageCache(pkg string) (string, bool) { for k, v := range remotePackageCache { if pkg == k || strings.HasPrefix(pkg, k+"/") { return v, true } } return pkg, false } func addToRemotePackageCache(pkg, v string) { remotePackageCache[pkg] = v } func parseImportFromBody(ur *url.URL, r io.ReadCloser) (u string, err error) { d := xml.NewDecoder(r) d.CharsetReader = charsetReader d.Strict = false var t xml.Token for { t, err = d.Token() if err != nil { if err == io.EOF { // If we hit the end of the markup and don't have anything // we return an error. err = vcs.ErrCannotDetectVCS } return } if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { return } if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { return } e, ok := t.(xml.StartElement) if !ok || !strings.EqualFold(e.Name.Local, "meta") { continue } if attrValue(e.Attr, "name") != "go-import" { continue } if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { // If the prefix supplied by the remote system isn't a prefix to the // url we're fetching return continue looking for more go-imports. // This will work for exact matches and prefixes. For example, // golang.org/x/net as a prefix will match for golang.org/x/net and // golang.org/x/net/context. vcsURL := ur.Host + ur.Path if !strings.HasPrefix(vcsURL, f[0]) { continue } else { u = f[0] return } } } } func charsetReader(charset string, input io.Reader) (io.Reader, error) { switch strings.ToLower(charset) { case "ascii": return input, nil default: return nil, fmt.Errorf("can't decode XML document using charset %q", charset) } } func attrValue(attrs []xml.Attr, name string) string { for _, a := range attrs { if strings.EqualFold(a.Name.Local, name) { return a.Value } } return "" } type vcsInfo struct { host string pattern string regex *regexp.Regexp } var vcsList = []*vcsInfo{ { host: "github.com", pattern: `^(?Pgithub\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`, }, { host: "bitbucket.org", pattern: `^(?Pbitbucket\.org/([A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`, }, { host: "launchpad.net", pattern: `^(?Plaunchpad\.net/(([A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`, }, { host: "git.launchpad.net", pattern: `^(?Pgit\.launchpad\.net/(([A-Za-z0-9_.\-]+)|~[A-Za-z0-9_.\-]+/(\+git|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))$`, }, { host: "hub.jazz.net", pattern: `^(?Phub\.jazz\.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`, }, { host: "go.googlesource.com", pattern: `^(?Pgo\.googlesource\.com/[A-Za-z0-9_.\-]+/?)$`, }, // TODO: Once Google Code becomes fully deprecated this can be removed. { host: "code.google.com", pattern: `^(?Pcode\.google\.com/[pr]/([a-z0-9\-]+)(\.([a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`, }, // Alternative Google setup for SVN. This is the previous structure but it still works... until Google Code goes away. { pattern: `^(?P[a-z0-9_\-.]+\.googlecode\.com/svn(/.*)?)$`, }, // Alternative Google setup. This is the previous structure but it still works... until Google Code goes away. { pattern: `^(?P[a-z0-9_\-.]+\.googlecode\.com/(git|hg))(/.*)?$`, }, // If none of the previous detect the type they will fall to this looking for the type in a generic sense // by the extension to the path. { pattern: `^(?P(?P([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`, }, } // BuildCtxt is a convenience wrapper for not having to import go/build // anywhere else type BuildCtxt struct { build.Context } // PackageName attempts to determine the name of the base package. // // If resolution fails, this will return "main". func (b *BuildCtxt) PackageName(base string) string { cwd, err := os.Getwd() if err != nil { return "main" } pkg, err := b.Import(base, cwd, 0) if err != nil { // There may not be any top level Go source files but the project may // still be within the GOPATH. if strings.HasPrefix(base, b.GOPATH) { p := strings.TrimPrefix(base, filepath.Join(b.GOPATH, "src")) return strings.Trim(p, string(os.PathSeparator)) } } return pkg.ImportPath } // GetBuildContext returns a build context from go/build. When the $GOROOT // variable is not set in the users environment it sets the context's root // path to the path returned by 'go env GOROOT'. // // TODO: This should be moved to the `dependency` package. func GetBuildContext() (*BuildCtxt, error) { if len(goRoot) == 0 { return nil, fmt.Errorf("GOROOT value not found. Please set the GOROOT " + "environment variable to use this command") } buildContext := &BuildCtxt{build.Default} // If we aren't resolving for the current system set to look at all // build modes. if !ResolveCurrent { // This tells the context scanning to skip filtering on +build flags or // file names. buildContext.UseAllFiles = true } buildContext.GOROOT = goRoot return buildContext, nil } // NormalizeName takes a package name and normalizes it to the top level package. // // For example, golang.org/x/crypto/ssh becomes golang.org/x/crypto. 'ssh' is // returned as extra data. // // FIXME: Is this deprecated? func NormalizeName(name string) (string, string) { // Fastpath check if a name in the GOROOT. There is an issue when a pkg // is in the GOROOT and GetRootFromPackage tries to look it up because it // expects remote names. b, err := GetBuildContext() if err == nil { p := filepath.Join(b.GOROOT, "src", name) if _, err := os.Stat(p); err == nil { return toSlash(name), "" } } name = toSlash(name) root := GetRootFromPackage(name) extra := strings.TrimPrefix(name, root) if len(extra) > 0 && extra != "/" { extra = strings.TrimPrefix(extra, "/") } else { // If extra is / (which is what it would be here) we want to return "" extra = "" } return root, extra } ================================================ FILE: util/util_test.go ================================================ package util import "testing" func TestGetRootFromPackage(t *testing.T) { urlList := map[string]string{ "github.com/Masterminds/VCSTestRepo": "github.com/Masterminds/VCSTestRepo", "bitbucket.org/mattfarina/testhgrepo": "bitbucket.org/mattfarina/testhgrepo", "launchpad.net/govcstestbzrrepo/trunk": "launchpad.net/govcstestbzrrepo/trunk", "launchpad.net/~mattfarina/+junk/mygovcstestbzrrepo": "launchpad.net/~mattfarina/+junk/mygovcstestbzrrepo", "launchpad.net/~mattfarina/+junk/mygovcstestbzrrepo/trunk": "launchpad.net/~mattfarina/+junk/mygovcstestbzrrepo", "git.launchpad.net/govcstestgitrepo": "git.launchpad.net/govcstestgitrepo", "git.launchpad.net/~mattfarina/+git/mygovcstestgitrepo": "git.launchpad.net/~mattfarina/+git/mygovcstestgitrepo", "hub.jazz.net/git/user/pkgname": "hub.jazz.net/git/user/pkgname", "hub.jazz.net/git/user/pkgname/subpkg/subpkg/subpkg": "hub.jazz.net/git/user/pkgname", "farbtastic.googlecode.com/svn/": "farbtastic.googlecode.com/svn/", "farbtastic.googlecode.com/svn/trunk": "farbtastic.googlecode.com/svn/trunk", "code.google.com/p/farbtastic": "code.google.com/p/farbtastic", "code.google.com/p/plotinum": "code.google.com/p/plotinum", "example.com/foo/bar.git": "example.com/foo/bar.git", "example.com/foo/bar.svn": "example.com/foo/bar.svn", "example.com/foo/bar/baz.bzr": "example.com/foo/bar/baz.bzr", "example.com/foo/bar/baz.hg": "example.com/foo/bar/baz.hg", "gopkg.in/mgo.v2": "gopkg.in/mgo.v2", "gopkg.in/mgo.v2/txn": "gopkg.in/mgo.v2", "gopkg.in/nowk/assert.v2": "gopkg.in/nowk/assert.v2", "gopkg.in/nowk/assert.v2/tests": "gopkg.in/nowk/assert.v2", "golang.org/x/net": "golang.org/x/net", "golang.org/x/net/context": "golang.org/x/net", } for u, c := range urlList { repo := GetRootFromPackage(u) if repo != c { t.Errorf("getRepoRootFromPackage expected %s but got %s", c, repo) } } } ================================================ FILE: vendor/github.com/Masterminds/semver/.travis.yml ================================================ language: go go: - 1.6.x - 1.7.x - 1.8.x - 1.9.x - 1.10.x - tip # Setting sudo access to false will let Travis CI use containers rather than # VMs to run the tests. For more details see: # - http://docs.travis-ci.com/user/workers/container-based-infrastructure/ # - http://docs.travis-ci.com/user/workers/standard-infrastructure/ sudo: false script: - make setup - make test notifications: webhooks: urls: - https://webhooks.gitter.im/e/06e3328629952dabe3e0 on_success: change # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always on_start: never # options: [always|never|change] default: always ================================================ FILE: vendor/github.com/Masterminds/semver/CHANGELOG.md ================================================ # 1.4.2 (2018-04-10) ## Changed - #72: Updated the docs to point to vert for a console appliaction - #71: Update the docs on pre-release comparator handling ## Fixed - #70: Fix the handling of pre-releases and the 0.0.0 release edge case # 1.4.1 (2018-04-02) ## Fixed - Fixed #64: Fix pre-release precedence issue (thanks @uudashr) # 1.4.0 (2017-10-04) ## Changed - #61: Update NewVersion to parse ints with a 64bit int size (thanks @zknill) # 1.3.1 (2017-07-10) ## Fixed - Fixed #57: number comparisons in prerelease sometimes inaccurate # 1.3.0 (2017-05-02) ## Added - #45: Added json (un)marshaling support (thanks @mh-cbon) - Stability marker. See https://masterminds.github.io/stability/ ## Fixed - #51: Fix handling of single digit tilde constraint (thanks @dgodd) ## Changed - #55: The godoc icon moved from png to svg # 1.2.3 (2017-04-03) ## Fixed - #46: Fixed 0.x.x and 0.0.x in constraints being treated as * # Release 1.2.2 (2016-12-13) ## Fixed - #34: Fixed issue where hyphen range was not working with pre-release parsing. # Release 1.2.1 (2016-11-28) ## Fixed - #24: Fixed edge case issue where constraint "> 0" does not handle "0.0.1-alpha" properly. # Release 1.2.0 (2016-11-04) ## Added - #20: Added MustParse function for versions (thanks @adamreese) - #15: Added increment methods on versions (thanks @mh-cbon) ## Fixed - Issue #21: Per the SemVer spec (section 9) a pre-release is unstable and might not satisfy the intended compatibility. The change here ignores pre-releases on constraint checks (e.g., ~ or ^) when a pre-release is not part of the constraint. For example, `^1.2.3` will ignore pre-releases while `^1.2.3-alpha` will include them. # Release 1.1.1 (2016-06-30) ## Changed - Issue #9: Speed up version comparison performance (thanks @sdboyer) - Issue #8: Added benchmarks (thanks @sdboyer) - Updated Go Report Card URL to new location - Updated Readme to add code snippet formatting (thanks @mh-cbon) - Updating tagging to v[SemVer] structure for compatibility with other tools. # Release 1.1.0 (2016-03-11) - Issue #2: Implemented validation to provide reasons a versions failed a constraint. # Release 1.0.1 (2015-12-31) - Fixed #1: * constraint failing on valid versions. # Release 1.0.0 (2015-10-20) - Initial release ================================================ FILE: vendor/github.com/Masterminds/semver/LICENSE.txt ================================================ The Masterminds Copyright (C) 2014-2015, Matt Butcher and Matt Farina 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. ================================================ FILE: vendor/github.com/Masterminds/semver/Makefile ================================================ .PHONY: setup setup: go get -u gopkg.in/alecthomas/gometalinter.v1 gometalinter.v1 --install .PHONY: test test: validate lint @echo "==> Running tests" go test -v .PHONY: validate validate: @echo "==> Running static validations" @gometalinter.v1 \ --disable-all \ --enable deadcode \ --severity deadcode:error \ --enable gofmt \ --enable gosimple \ --enable ineffassign \ --enable misspell \ --enable vet \ --tests \ --vendor \ --deadline 60s \ ./... || exit_code=1 .PHONY: lint lint: @echo "==> Running linters" @gometalinter.v1 \ --disable-all \ --enable golint \ --vendor \ --deadline 60s \ ./... || : ================================================ FILE: vendor/github.com/Masterminds/semver/README.md ================================================ # SemVer The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to: * Parse semantic versions * Sort semantic versions * Check if a semantic version fits within a set of constraints * Optionally work with a `v` prefix [![Stability: Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html) [![Build Status](https://travis-ci.org/Masterminds/semver.svg)](https://travis-ci.org/Masterminds/semver) [![Build status](https://ci.appveyor.com/api/projects/status/jfk66lib7hb985k8/branch/master?svg=true&passingText=windows%20build%20passing&failingText=windows%20build%20failing)](https://ci.appveyor.com/project/mattfarina/semver/branch/master) [![GoDoc](https://godoc.org/github.com/Masterminds/semver?status.svg)](https://godoc.org/github.com/Masterminds/semver) [![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/semver)](https://goreportcard.com/report/github.com/Masterminds/semver) ## Parsing Semantic Versions To parse a semantic version use the `NewVersion` function. For example, ```go v, err := semver.NewVersion("1.2.3-beta.1+build345") ``` If there is an error the version wasn't parseable. The version object has methods to get the parts of the version, compare it to other versions, convert the version back into a string, and get the original string. For more details please see the [documentation](https://godoc.org/github.com/Masterminds/semver). ## Sorting Semantic Versions A set of versions can be sorted using the [`sort`](https://golang.org/pkg/sort/) package from the standard library. For example, ```go raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",} vs := make([]*semver.Version, len(raw)) for i, r := range raw { v, err := semver.NewVersion(r) if err != nil { t.Errorf("Error parsing version: %s", err) } vs[i] = v } sort.Sort(semver.Collection(vs)) ``` ## Checking Version Constraints Checking a version against version constraints is one of the most featureful parts of the package. ```go c, err := semver.NewConstraint(">= 1.2.3") if err != nil { // Handle constraint not being parseable. } v, _ := semver.NewVersion("1.3") if err != nil { // Handle version not being parseable. } // Check if the version meets the constraints. The a variable will be true. a := c.Check(v) ``` ## Basic Comparisons There are two elements to the comparisons. First, a comparison string is a list of comma separated and comparisons. These are then separated by || separated or comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a comparison that's greater than or equal to 1.2 and less than 3.0.0 or is greater than or equal to 4.2.3. The basic comparisons are: * `=`: equal (aliased to no operator) * `!=`: not equal * `>`: greater than * `<`: less than * `>=`: greater than or equal to * `<=`: less than or equal to _Note, according to the Semantic Version specification pre-releases may not be API compliant with their release counterpart. It says,_ > _A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version._ _SemVer comparisons without a pre-release value will skip pre-release versions. For example, `>1.2.3` will skip pre-releases when looking at a list of values while `>1.2.3-alpha.1` will evaluate pre-releases._ ## Hyphen Range Comparisons There are multiple methods to handle ranges and the first is hyphens ranges. These look like: * `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5` * `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5` ## Wildcards In Comparisons The `x`, `X`, and `*` characters can be used as a wildcard character. This works for all comparison operators. When used on the `=` operator it falls back to the pack level comparison (see tilde below). For example, * `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` * `>= 1.2.x` is equivalent to `>= 1.2.0` * `<= 2.x` is equivalent to `<= 3` * `*` is equivalent to `>= 0.0.0` ## Tilde Range Comparisons (Patch) The tilde (`~`) comparison operator is for patch level ranges when a minor version is specified and major level changes when the minor number is missing. For example, * `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0` * `~1` is equivalent to `>= 1, < 2` * `~2.3` is equivalent to `>= 2.3, < 2.4` * `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` * `~1.x` is equivalent to `>= 1, < 2` ## Caret Range Comparisons (Major) The caret (`^`) comparison operator is for major level changes. This is useful when comparisons of API versions as a major change is API breaking. For example, * `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` * `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` * `^2.3` is equivalent to `>= 2.3, < 3` * `^2.x` is equivalent to `>= 2.0.0, < 3` # Validation In addition to testing a version against a constraint, a version can be validated against a constraint. When validation fails a slice of errors containing why a version didn't meet the constraint is returned. For example, ```go c, err := semver.NewConstraint("<= 1.2.3, >= 1.4") if err != nil { // Handle constraint not being parseable. } v, _ := semver.NewVersion("1.3") if err != nil { // Handle version not being parseable. } // Validate a version against a constraint. a, msgs := c.Validate(v) // a is false for _, m := range msgs { fmt.Println(m) // Loops over the errors which would read // "1.3 is greater than 1.2.3" // "1.3 is less than 1.4" } ``` # Contribute If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues) or [create a pull request](https://github.com/Masterminds/semver/pulls). ================================================ FILE: vendor/github.com/Masterminds/semver/appveyor.yml ================================================ version: build-{build}.{branch} clone_folder: C:\gopath\src\github.com\Masterminds\semver shallow_clone: true environment: GOPATH: C:\gopath platform: - x64 install: - go version - go env - go get -u gopkg.in/alecthomas/gometalinter.v1 - set PATH=%PATH%;%GOPATH%\bin - gometalinter.v1.exe --install build_script: - go install -v ./... test_script: - "gometalinter.v1 \ --disable-all \ --enable deadcode \ --severity deadcode:error \ --enable gofmt \ --enable gosimple \ --enable ineffassign \ --enable misspell \ --enable vet \ --tests \ --vendor \ --deadline 60s \ ./... || exit_code=1" - "gometalinter.v1 \ --disable-all \ --enable golint \ --vendor \ --deadline 60s \ ./... || :" - go test -v deploy: off ================================================ FILE: vendor/github.com/Masterminds/semver/benchmark_test.go ================================================ package semver_test import ( "testing" "github.com/Masterminds/semver" ) /* Constraint creation benchmarks */ func benchNewConstraint(c string, b *testing.B) { for i := 0; i < b.N; i++ { semver.NewConstraint(c) } } func BenchmarkNewConstraintUnary(b *testing.B) { benchNewConstraint("=2.0", b) } func BenchmarkNewConstraintTilde(b *testing.B) { benchNewConstraint("~2.0.0", b) } func BenchmarkNewConstraintCaret(b *testing.B) { benchNewConstraint("^2.0.0", b) } func BenchmarkNewConstraintWildcard(b *testing.B) { benchNewConstraint("1.x", b) } func BenchmarkNewConstraintRange(b *testing.B) { benchNewConstraint(">=2.1.x, <3.1.0", b) } func BenchmarkNewConstraintUnion(b *testing.B) { benchNewConstraint("~2.0.0 || =3.1.0", b) } /* Check benchmarks */ func benchCheckVersion(c, v string, b *testing.B) { version, _ := semver.NewVersion(v) constraint, _ := semver.NewConstraint(c) for i := 0; i < b.N; i++ { constraint.Check(version) } } func BenchmarkCheckVersionUnary(b *testing.B) { benchCheckVersion("=2.0", "2.0.0", b) } func BenchmarkCheckVersionTilde(b *testing.B) { benchCheckVersion("~2.0.0", "2.0.5", b) } func BenchmarkCheckVersionCaret(b *testing.B) { benchCheckVersion("^2.0.0", "2.1.0", b) } func BenchmarkCheckVersionWildcard(b *testing.B) { benchCheckVersion("1.x", "1.4.0", b) } func BenchmarkCheckVersionRange(b *testing.B) { benchCheckVersion(">=2.1.x, <3.1.0", "2.4.5", b) } func BenchmarkCheckVersionUnion(b *testing.B) { benchCheckVersion("~2.0.0 || =3.1.0", "3.1.0", b) } func benchValidateVersion(c, v string, b *testing.B) { version, _ := semver.NewVersion(v) constraint, _ := semver.NewConstraint(c) for i := 0; i < b.N; i++ { constraint.Validate(version) } } /* Validate benchmarks, including fails */ func BenchmarkValidateVersionUnary(b *testing.B) { benchValidateVersion("=2.0", "2.0.0", b) } func BenchmarkValidateVersionUnaryFail(b *testing.B) { benchValidateVersion("=2.0", "2.0.1", b) } func BenchmarkValidateVersionTilde(b *testing.B) { benchValidateVersion("~2.0.0", "2.0.5", b) } func BenchmarkValidateVersionTildeFail(b *testing.B) { benchValidateVersion("~2.0.0", "1.0.5", b) } func BenchmarkValidateVersionCaret(b *testing.B) { benchValidateVersion("^2.0.0", "2.1.0", b) } func BenchmarkValidateVersionCaretFail(b *testing.B) { benchValidateVersion("^2.0.0", "4.1.0", b) } func BenchmarkValidateVersionWildcard(b *testing.B) { benchValidateVersion("1.x", "1.4.0", b) } func BenchmarkValidateVersionWildcardFail(b *testing.B) { benchValidateVersion("1.x", "2.4.0", b) } func BenchmarkValidateVersionRange(b *testing.B) { benchValidateVersion(">=2.1.x, <3.1.0", "2.4.5", b) } func BenchmarkValidateVersionRangeFail(b *testing.B) { benchValidateVersion(">=2.1.x, <3.1.0", "1.4.5", b) } func BenchmarkValidateVersionUnion(b *testing.B) { benchValidateVersion("~2.0.0 || =3.1.0", "3.1.0", b) } func BenchmarkValidateVersionUnionFail(b *testing.B) { benchValidateVersion("~2.0.0 || =3.1.0", "3.1.1", b) } /* Version creation benchmarks */ func benchNewVersion(v string, b *testing.B) { for i := 0; i < b.N; i++ { semver.NewVersion(v) } } func BenchmarkNewVersionSimple(b *testing.B) { benchNewVersion("1.0.0", b) } func BenchmarkNewVersionPre(b *testing.B) { benchNewVersion("1.0.0-alpha", b) } func BenchmarkNewVersionMeta(b *testing.B) { benchNewVersion("1.0.0+metadata", b) } func BenchmarkNewVersionMetaDash(b *testing.B) { benchNewVersion("1.0.0+metadata-dash", b) } ================================================ FILE: vendor/github.com/Masterminds/semver/collection.go ================================================ package semver // Collection is a collection of Version instances and implements the sort // interface. See the sort package for more details. // https://golang.org/pkg/sort/ type Collection []*Version // Len returns the length of a collection. The number of Version instances // on the slice. func (c Collection) Len() int { return len(c) } // Less is needed for the sort interface to compare two Version objects on the // slice. If checks if one is less than the other. func (c Collection) Less(i, j int) bool { return c[i].LessThan(c[j]) } // Swap is needed for the sort interface to replace the Version objects // at two different positions in the slice. func (c Collection) Swap(i, j int) { c[i], c[j] = c[j], c[i] } ================================================ FILE: vendor/github.com/Masterminds/semver/collection_test.go ================================================ package semver import ( "reflect" "sort" "testing" ) func TestCollection(t *testing.T) { raw := []string{ "1.2.3", "1.0", "1.3", "2", "0.4.2", } vs := make([]*Version, len(raw)) for i, r := range raw { v, err := NewVersion(r) if err != nil { t.Errorf("Error parsing version: %s", err) } vs[i] = v } sort.Sort(Collection(vs)) e := []string{ "0.4.2", "1.0.0", "1.2.3", "1.3.0", "2.0.0", } a := make([]string, len(vs)) for i, v := range vs { a[i] = v.String() } if !reflect.DeepEqual(a, e) { t.Error("Sorting Collection failed") } } ================================================ FILE: vendor/github.com/Masterminds/semver/constraints.go ================================================ package semver import ( "errors" "fmt" "regexp" "strings" ) // Constraints is one or more constraint that a semantic version can be // checked against. type Constraints struct { constraints [][]*constraint } // NewConstraint returns a Constraints instance that a Version instance can // be checked against. If there is a parse error it will be returned. func NewConstraint(c string) (*Constraints, error) { // Rewrite - ranges into a comparison operation. c = rewriteRange(c) ors := strings.Split(c, "||") or := make([][]*constraint, len(ors)) for k, v := range ors { cs := strings.Split(v, ",") result := make([]*constraint, len(cs)) for i, s := range cs { pc, err := parseConstraint(s) if err != nil { return nil, err } result[i] = pc } or[k] = result } o := &Constraints{constraints: or} return o, nil } // Check tests if a version satisfies the constraints. func (cs Constraints) Check(v *Version) bool { // loop over the ORs and check the inner ANDs for _, o := range cs.constraints { joy := true for _, c := range o { if !c.check(v) { joy = false break } } if joy { return true } } return false } // Validate checks if a version satisfies a constraint. If not a slice of // reasons for the failure are returned in addition to a bool. func (cs Constraints) Validate(v *Version) (bool, []error) { // loop over the ORs and check the inner ANDs var e []error for _, o := range cs.constraints { joy := true for _, c := range o { if !c.check(v) { em := fmt.Errorf(c.msg, v, c.orig) e = append(e, em) joy = false } } if joy { return true, []error{} } } return false, e } var constraintOps map[string]cfunc var constraintMsg map[string]string var constraintRegex *regexp.Regexp func init() { constraintOps = map[string]cfunc{ "": constraintTildeOrEqual, "=": constraintTildeOrEqual, "!=": constraintNotEqual, ">": constraintGreaterThan, "<": constraintLessThan, ">=": constraintGreaterThanEqual, "=>": constraintGreaterThanEqual, "<=": constraintLessThanEqual, "=<": constraintLessThanEqual, "~": constraintTilde, "~>": constraintTilde, "^": constraintCaret, } constraintMsg = map[string]string{ "": "%s is not equal to %s", "=": "%s is not equal to %s", "!=": "%s is equal to %s", ">": "%s is less than or equal to %s", "<": "%s is greater than or equal to %s", ">=": "%s is less than %s", "=>": "%s is less than %s", "<=": "%s is greater than %s", "=<": "%s is greater than %s", "~": "%s does not have same major and minor version as %s", "~>": "%s does not have same major and minor version as %s", "^": "%s does not have same major version as %s", } ops := make([]string, 0, len(constraintOps)) for k := range constraintOps { ops = append(ops, regexp.QuoteMeta(k)) } constraintRegex = regexp.MustCompile(fmt.Sprintf( `^\s*(%s)\s*(%s)\s*$`, strings.Join(ops, "|"), cvRegex)) constraintRangeRegex = regexp.MustCompile(fmt.Sprintf( `\s*(%s)\s+-\s+(%s)\s*`, cvRegex, cvRegex)) } // An individual constraint type constraint struct { // The callback function for the restraint. It performs the logic for // the constraint. function cfunc msg string // The version used in the constraint check. For example, if a constraint // is '<= 2.0.0' the con a version instance representing 2.0.0. con *Version // The original parsed version (e.g., 4.x from != 4.x) orig string // When an x is used as part of the version (e.g., 1.x) minorDirty bool dirty bool patchDirty bool } // Check if a version meets the constraint func (c *constraint) check(v *Version) bool { return c.function(v, c) } type cfunc func(v *Version, c *constraint) bool func parseConstraint(c string) (*constraint, error) { m := constraintRegex.FindStringSubmatch(c) if m == nil { return nil, fmt.Errorf("improper constraint: %s", c) } ver := m[2] orig := ver minorDirty := false patchDirty := false dirty := false if isX(m[3]) { ver = "0.0.0" dirty = true } else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" { minorDirty = true dirty = true ver = fmt.Sprintf("%s.0.0%s", m[3], m[6]) } else if isX(strings.TrimPrefix(m[5], ".")) { dirty = true patchDirty = true ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6]) } con, err := NewVersion(ver) if err != nil { // The constraintRegex should catch any regex parsing errors. So, // we should never get here. return nil, errors.New("constraint Parser Error") } cs := &constraint{ function: constraintOps[m[1]], msg: constraintMsg[m[1]], con: con, orig: orig, minorDirty: minorDirty, patchDirty: patchDirty, dirty: dirty, } return cs, nil } // Constraint functions func constraintNotEqual(v *Version, c *constraint) bool { if c.dirty { // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. if v.Prerelease() != "" && c.con.Prerelease() == "" { return false } if c.con.Major() != v.Major() { return true } if c.con.Minor() != v.Minor() && !c.minorDirty { return true } else if c.minorDirty { return false } return false } return !v.Equal(c.con) } func constraintGreaterThan(v *Version, c *constraint) bool { // An edge case the constraint is 0.0.0 and the version is 0.0.0-someprerelease // exists. This that case. if !isNonZero(c.con) && isNonZero(v) { return true } // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. if v.Prerelease() != "" && c.con.Prerelease() == "" { return false } return v.Compare(c.con) == 1 } func constraintLessThan(v *Version, c *constraint) bool { // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. if v.Prerelease() != "" && c.con.Prerelease() == "" { return false } if !c.dirty { return v.Compare(c.con) < 0 } if v.Major() > c.con.Major() { return false } else if v.Minor() > c.con.Minor() && !c.minorDirty { return false } return true } func constraintGreaterThanEqual(v *Version, c *constraint) bool { // An edge case the constraint is 0.0.0 and the version is 0.0.0-someprerelease // exists. This that case. if !isNonZero(c.con) && isNonZero(v) { return true } // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. if v.Prerelease() != "" && c.con.Prerelease() == "" { return false } return v.Compare(c.con) >= 0 } func constraintLessThanEqual(v *Version, c *constraint) bool { // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. if v.Prerelease() != "" && c.con.Prerelease() == "" { return false } if !c.dirty { return v.Compare(c.con) <= 0 } if v.Major() > c.con.Major() { return false } else if v.Minor() > c.con.Minor() && !c.minorDirty { return false } return true } // ~*, ~>* --> >= 0.0.0 (any) // ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0 // ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0 // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0 // ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0 // ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0 func constraintTilde(v *Version, c *constraint) bool { // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. if v.Prerelease() != "" && c.con.Prerelease() == "" { return false } if v.LessThan(c.con) { return false } // ~0.0.0 is a special case where all constraints are accepted. It's // equivalent to >= 0.0.0. if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 && !c.minorDirty && !c.patchDirty { return true } if v.Major() != c.con.Major() { return false } if v.Minor() != c.con.Minor() && !c.minorDirty { return false } return true } // When there is a .x (dirty) status it automatically opts in to ~. Otherwise // it's a straight = func constraintTildeOrEqual(v *Version, c *constraint) bool { // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. if v.Prerelease() != "" && c.con.Prerelease() == "" { return false } if c.dirty { c.msg = constraintMsg["~"] return constraintTilde(v, c) } return v.Equal(c.con) } // ^* --> (any) // ^2, ^2.x, ^2.x.x --> >=2.0.0, <3.0.0 // ^2.0, ^2.0.x --> >=2.0.0, <3.0.0 // ^1.2, ^1.2.x --> >=1.2.0, <2.0.0 // ^1.2.3 --> >=1.2.3, <2.0.0 // ^1.2.0 --> >=1.2.0, <2.0.0 func constraintCaret(v *Version, c *constraint) bool { // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. if v.Prerelease() != "" && c.con.Prerelease() == "" { return false } if v.LessThan(c.con) { return false } if v.Major() != c.con.Major() { return false } return true } var constraintRangeRegex *regexp.Regexp const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` + `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` func isX(x string) bool { switch x { case "x", "*", "X": return true default: return false } } func rewriteRange(i string) string { m := constraintRangeRegex.FindAllStringSubmatch(i, -1) if m == nil { return i } o := i for _, v := range m { t := fmt.Sprintf(">= %s, <= %s", v[1], v[11]) o = strings.Replace(o, v[0], t, 1) } return o } // Detect if a version is not zero (0.0.0) func isNonZero(v *Version) bool { if v.Major() != 0 || v.Minor() != 0 || v.Patch() != 0 || v.Prerelease() != "" { return true } return false } ================================================ FILE: vendor/github.com/Masterminds/semver/constraints_test.go ================================================ package semver import ( "reflect" "testing" ) func TestParseConstraint(t *testing.T) { tests := []struct { in string f cfunc v string err bool }{ {">= 1.2", constraintGreaterThanEqual, "1.2.0", false}, {"1.0", constraintTildeOrEqual, "1.0.0", false}, {"foo", nil, "", true}, {"<= 1.2", constraintLessThanEqual, "1.2.0", false}, {"=< 1.2", constraintLessThanEqual, "1.2.0", false}, {"=> 1.2", constraintGreaterThanEqual, "1.2.0", false}, {"v1.2", constraintTildeOrEqual, "1.2.0", false}, {"=1.5", constraintTildeOrEqual, "1.5.0", false}, {"> 1.3", constraintGreaterThan, "1.3.0", false}, {"< 1.4.1", constraintLessThan, "1.4.1", false}, } for _, tc := range tests { c, err := parseConstraint(tc.in) if tc.err && err == nil { t.Errorf("Expected error for %s didn't occur", tc.in) } else if !tc.err && err != nil { t.Errorf("Unexpected error for %s", tc.in) } // If an error was expected continue the loop and don't try the other // tests as they will cause errors. if tc.err { continue } if tc.v != c.con.String() { t.Errorf("Incorrect version found on %s", tc.in) } f1 := reflect.ValueOf(tc.f) f2 := reflect.ValueOf(c.function) if f1 != f2 { t.Errorf("Wrong constraint found for %s", tc.in) } } } func TestConstraintCheck(t *testing.T) { tests := []struct { constraint string version string check bool }{ {"= 2.0", "1.2.3", false}, {"= 2.0", "2.0.0", true}, {"4.1", "4.1.0", true}, {"!=4.1", "4.1.0", false}, {"!=4.1", "5.1.0", true}, {">1.1", "4.1.0", true}, {">1.1", "1.1.0", false}, {"<1.1", "0.1.0", true}, {"<1.1", "1.1.0", false}, {"<1.1", "1.1.1", false}, {">=1.1", "4.1.0", true}, {">=1.1", "1.1.0", true}, {">=1.1", "0.0.9", false}, {"<=1.1", "0.1.0", true}, {"<=1.1", "1.1.0", true}, {"<=1.1", "1.1.1", false}, {">0", "0.0.1-alpha", true}, {">=0", "0.0.1-alpha", true}, {">0", "0", false}, {">=0", "0", true}, {"=0", "1", false}, } for _, tc := range tests { c, err := parseConstraint(tc.constraint) if err != nil { t.Errorf("err: %s", err) continue } v, err := NewVersion(tc.version) if err != nil { t.Errorf("err: %s", err) continue } a := c.check(v) if a != tc.check { t.Errorf("Constraint %q failing with %q", tc.constraint, tc.version) } } } func TestNewConstraint(t *testing.T) { tests := []struct { input string ors int count int err bool }{ {">= 1.1", 1, 1, false}, {"2.0", 1, 1, false}, {"v2.3.5-20161202202307-sha.e8fc5e5", 1, 1, false}, {">= bar", 0, 0, true}, {">= 1.2.3, < 2.0", 1, 2, false}, {">= 1.2.3, < 2.0 || => 3.0, < 4", 2, 2, false}, // The 3 - 4 should be broken into 2 by the range rewriting {"3 - 4 || => 3.0, < 4", 2, 2, false}, } for _, tc := range tests { v, err := NewConstraint(tc.input) if tc.err && err == nil { t.Errorf("expected but did not get error for: %s", tc.input) continue } else if !tc.err && err != nil { t.Errorf("unexpectederror for input %s: %s", tc.input, err) continue } if tc.err { continue } l := len(v.constraints) if tc.ors != l { t.Errorf("Expected %s to have %d ORs but got %d", tc.input, tc.ors, l) } l = len(v.constraints[0]) if tc.count != l { t.Errorf("Expected %s to have %d constraints but got %d", tc.input, tc.count, l) } } } func TestConstraintsCheck(t *testing.T) { tests := []struct { constraint string version string check bool }{ {"*", "1.2.3", true}, {"~0.0.0", "1.2.3", true}, {"0.x.x", "1.2.3", false}, {"0.0.x", "1.2.3", false}, {"0.0.0", "1.2.3", false}, {"*", "1.2.3", true}, {"^0.0.0", "1.2.3", false}, {"= 2.0", "1.2.3", false}, {"= 2.0", "2.0.0", true}, {"4.1", "4.1.0", true}, {"4.1.x", "4.1.3", true}, {"1.x", "1.4", true}, {"!=4.1", "4.1.0", false}, {"!=4.1-alpha", "4.1.0-alpha", false}, {"!=4.1-alpha", "4.1.0", true}, {"!=4.1", "5.1.0", true}, {"!=4.x", "5.1.0", true}, {"!=4.x", "4.1.0", false}, {"!=4.1.x", "4.2.0", true}, {"!=4.2.x", "4.2.3", false}, {">1.1", "4.1.0", true}, {">1.1", "1.1.0", false}, {"<1.1", "0.1.0", true}, {"<1.1", "1.1.0", false}, {"<1.1", "1.1.1", false}, {"<1.x", "1.1.1", true}, {"<1.x", "2.1.1", false}, {"<1.1.x", "1.2.1", false}, {"<1.1.x", "1.1.500", true}, {"<1.2.x", "1.1.1", true}, {">=1.1", "4.1.0", true}, {">=1.1", "4.1.0-beta", false}, {">=1.1", "1.1.0", true}, {">=1.1", "0.0.9", false}, {"<=1.1", "0.1.0", true}, {"<=1.1", "0.1.0-alpha", false}, {"<=1.1-a", "0.1.0-alpha", true}, {"<=1.1", "1.1.0", true}, {"<=1.x", "1.1.0", true}, {"<=2.x", "3.1.0", false}, {"<=1.1", "1.1.1", false}, {"<=1.1.x", "1.2.500", false}, {">1.1, <2", "1.1.1", true}, {">1.1, <3", "4.3.2", false}, {">=1.1, <2, !=1.2.3", "1.2.3", false}, {">=1.1, <2, !=1.2.3 || > 3", "3.1.2", true}, {">=1.1, <2, !=1.2.3 || >= 3", "3.0.0", true}, {">=1.1, <2, !=1.2.3 || > 3", "3.0.0", false}, {">=1.1, <2, !=1.2.3 || > 3", "1.2.3", false}, {"1.1 - 2", "1.1.1", true}, {"1.1-3", "4.3.2", false}, {"^1.1", "1.1.1", true}, {"^1.1", "4.3.2", false}, {"^1.x", "1.1.1", true}, {"^2.x", "1.1.1", false}, {"^1.x", "2.1.1", false}, {"^1.x", "1.1.1-beta1", false}, {"^1.1.2-alpha", "1.2.1-beta1", true}, {"^1.2.x-alpha", "1.1.1-beta1", false}, {"~*", "2.1.1", true}, {"~1", "2.1.1", false}, {"~1", "1.3.5", true}, {"~1", "1.4", true}, {"~1.x", "2.1.1", false}, {"~1.x", "1.3.5", true}, {"~1.x", "1.4", true}, {"~1.1", "1.1.1", true}, {"~1.1", "1.1.1-alpha", false}, {"~1.1-alpha", "1.1.1-beta", true}, {"~1.1.1-beta", "1.1.1-alpha", false}, {"~1.1.1-beta", "1.1.1", true}, {"~1.2.3", "1.2.5", true}, {"~1.2.3", "1.2.2", false}, {"~1.2.3", "1.3.2", false}, {"~1.1", "1.2.3", false}, {"~1.3", "2.4.5", false}, } for _, tc := range tests { c, err := NewConstraint(tc.constraint) if err != nil { t.Errorf("err: %s", err) continue } v, err := NewVersion(tc.version) if err != nil { t.Errorf("err: %s", err) continue } a := c.Check(v) if a != tc.check { t.Errorf("Constraint '%s' failing with '%s'", tc.constraint, tc.version) } } } func TestRewriteRange(t *testing.T) { tests := []struct { c string nc string }{ {"2 - 3", ">= 2, <= 3"}, {"2 - 3, 2 - 3", ">= 2, <= 3,>= 2, <= 3"}, {"2 - 3, 4.0.0 - 5.1", ">= 2, <= 3,>= 4.0.0, <= 5.1"}, } for _, tc := range tests { o := rewriteRange(tc.c) if o != tc.nc { t.Errorf("Range %s rewritten incorrectly as '%s'", tc.c, o) } } } func TestIsX(t *testing.T) { tests := []struct { t string c bool }{ {"A", false}, {"%", false}, {"X", true}, {"x", true}, {"*", true}, } for _, tc := range tests { a := isX(tc.t) if a != tc.c { t.Errorf("Function isX error on %s", tc.t) } } } func TestConstraintsValidate(t *testing.T) { tests := []struct { constraint string version string check bool }{ {"*", "1.2.3", true}, {"~0.0.0", "1.2.3", true}, {"= 2.0", "1.2.3", false}, {"= 2.0", "2.0.0", true}, {"4.1", "4.1.0", true}, {"4.1.x", "4.1.3", true}, {"1.x", "1.4", true}, {"!=4.1", "4.1.0", false}, {"!=4.1", "5.1.0", true}, {"!=4.x", "5.1.0", true}, {"!=4.x", "4.1.0", false}, {"!=4.1.x", "4.2.0", true}, {"!=4.2.x", "4.2.3", false}, {">1.1", "4.1.0", true}, {">1.1", "1.1.0", false}, {"<1.1", "0.1.0", true}, {"<1.1", "1.1.0", false}, {"<1.1", "1.1.1", false}, {"<1.x", "1.1.1", true}, {"<1.x", "2.1.1", false}, {"<1.1.x", "1.2.1", false}, {"<1.1.x", "1.1.500", true}, {"<1.2.x", "1.1.1", true}, {">=1.1", "4.1.0", true}, {">=1.1", "1.1.0", true}, {">=1.1", "0.0.9", false}, {"<=1.1", "0.1.0", true}, {"<=1.1", "1.1.0", true}, {"<=1.x", "1.1.0", true}, {"<=2.x", "3.1.0", false}, {"<=1.1", "1.1.1", false}, {"<=1.1.x", "1.2.500", false}, {">1.1, <2", "1.1.1", true}, {">1.1, <3", "4.3.2", false}, {">=1.1, <2, !=1.2.3", "1.2.3", false}, {">=1.1, <2, !=1.2.3 || > 3", "3.1.2", true}, {">=1.1, <2, !=1.2.3 || >= 3", "3.0.0", true}, {">=1.1, <2, !=1.2.3 || > 3", "3.0.0", false}, {">=1.1, <2, !=1.2.3 || > 3", "1.2.3", false}, {"1.1 - 2", "1.1.1", true}, {"1.1-3", "4.3.2", false}, {"^1.1", "1.1.1", true}, {"^1.1", "1.1.1-alpha", false}, {"^1.1.1-alpha", "1.1.1-beta", true}, {"^1.1.1-beta", "1.1.1-alpha", false}, {"^1.1", "4.3.2", false}, {"^1.x", "1.1.1", true}, {"^2.x", "1.1.1", false}, {"^1.x", "2.1.1", false}, {"~*", "2.1.1", true}, {"~1", "2.1.1", false}, {"~1", "1.3.5", true}, {"~1", "1.3.5-beta", false}, {"~1.x", "2.1.1", false}, {"~1.x", "1.3.5", true}, {"~1.x", "1.3.5-beta", false}, {"~1.3.6-alpha", "1.3.5-beta", false}, {"~1.3.5-alpha", "1.3.5-beta", true}, {"~1.3.5-beta", "1.3.5-alpha", false}, {"~1.x", "1.4", true}, {"~1.1", "1.1.1", true}, {"~1.2.3", "1.2.5", true}, {"~1.2.3", "1.2.2", false}, {"~1.2.3", "1.3.2", false}, {"~1.1", "1.2.3", false}, {"~1.3", "2.4.5", false}, } for _, tc := range tests { c, err := NewConstraint(tc.constraint) if err != nil { t.Errorf("err: %s", err) continue } v, err := NewVersion(tc.version) if err != nil { t.Errorf("err: %s", err) continue } a, msgs := c.Validate(v) if a != tc.check { t.Errorf("Constraint '%s' failing with '%s'", tc.constraint, tc.version) } else if !a && len(msgs) == 0 { t.Errorf("%q failed with %q but no errors returned", tc.constraint, tc.version) } // if a == false { // for _, m := range msgs { // t.Errorf("%s", m) // } // } } v, err := NewVersion("1.2.3") if err != nil { t.Errorf("err: %s", err) } c, err := NewConstraint("!= 1.2.5, ^2, <= 1.1.x") if err != nil { t.Errorf("err: %s", err) } _, msgs := c.Validate(v) if len(msgs) != 2 { t.Error("Invalid number of validations found") } e := msgs[0].Error() if e != "1.2.3 does not have same major version as 2" { t.Error("Did not get expected message: 1.2.3 does not have same major version as 2") } e = msgs[1].Error() if e != "1.2.3 is greater than 1.1.x" { t.Error("Did not get expected message: 1.2.3 is greater than 1.1.x") } tests2 := []struct { constraint, version, msg string }{ {"= 2.0", "1.2.3", "1.2.3 is not equal to 2.0"}, {"!=4.1", "4.1.0", "4.1.0 is equal to 4.1"}, {"!=4.x", "4.1.0", "4.1.0 is equal to 4.x"}, {"!=4.2.x", "4.2.3", "4.2.3 is equal to 4.2.x"}, {">1.1", "1.1.0", "1.1.0 is less than or equal to 1.1"}, {"<1.1", "1.1.0", "1.1.0 is greater than or equal to 1.1"}, {"<1.1", "1.1.1", "1.1.1 is greater than or equal to 1.1"}, {"<1.x", "2.1.1", "2.1.1 is greater than or equal to 1.x"}, {"<1.1.x", "1.2.1", "1.2.1 is greater than or equal to 1.1.x"}, {">=1.1", "0.0.9", "0.0.9 is less than 1.1"}, {"<=2.x", "3.1.0", "3.1.0 is greater than 2.x"}, {"<=1.1", "1.1.1", "1.1.1 is greater than 1.1"}, {"<=1.1.x", "1.2.500", "1.2.500 is greater than 1.1.x"}, {">1.1, <3", "4.3.2", "4.3.2 is greater than or equal to 3"}, {">=1.1, <2, !=1.2.3", "1.2.3", "1.2.3 is equal to 1.2.3"}, {">=1.1, <2, !=1.2.3 || > 3", "3.0.0", "3.0.0 is greater than or equal to 2"}, {">=1.1, <2, !=1.2.3 || > 3", "1.2.3", "1.2.3 is equal to 1.2.3"}, {"1.1 - 3", "4.3.2", "4.3.2 is greater than 3"}, {"^1.1", "4.3.2", "4.3.2 does not have same major version as 1.1"}, {"^2.x", "1.1.1", "1.1.1 does not have same major version as 2.x"}, {"^1.x", "2.1.1", "2.1.1 does not have same major version as 1.x"}, {"~1", "2.1.2", "2.1.2 does not have same major and minor version as 1"}, {"~1.x", "2.1.1", "2.1.1 does not have same major and minor version as 1.x"}, {"~1.2.3", "1.2.2", "1.2.2 does not have same major and minor version as 1.2.3"}, {"~1.2.3", "1.3.2", "1.3.2 does not have same major and minor version as 1.2.3"}, {"~1.1", "1.2.3", "1.2.3 does not have same major and minor version as 1.1"}, {"~1.3", "2.4.5", "2.4.5 does not have same major and minor version as 1.3"}, } for _, tc := range tests2 { c, err := NewConstraint(tc.constraint) if err != nil { t.Errorf("err: %s", err) continue } v, err := NewVersion(tc.version) if err != nil { t.Errorf("err: %s", err) continue } _, msgs := c.Validate(v) e := msgs[0].Error() if e != tc.msg { t.Errorf("Did not get expected message %q: %s", tc.msg, e) } } } ================================================ FILE: vendor/github.com/Masterminds/semver/doc.go ================================================ /* Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go. Specifically it provides the ability to: * Parse semantic versions * Sort semantic versions * Check if a semantic version fits within a set of constraints * Optionally work with a `v` prefix Parsing Semantic Versions To parse a semantic version use the `NewVersion` function. For example, v, err := semver.NewVersion("1.2.3-beta.1+build345") If there is an error the version wasn't parseable. The version object has methods to get the parts of the version, compare it to other versions, convert the version back into a string, and get the original string. For more details please see the documentation at https://godoc.org/github.com/Masterminds/semver. Sorting Semantic Versions A set of versions can be sorted using the `sort` package from the standard library. For example, raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",} vs := make([]*semver.Version, len(raw)) for i, r := range raw { v, err := semver.NewVersion(r) if err != nil { t.Errorf("Error parsing version: %s", err) } vs[i] = v } sort.Sort(semver.Collection(vs)) Checking Version Constraints Checking a version against version constraints is one of the most featureful parts of the package. c, err := semver.NewConstraint(">= 1.2.3") if err != nil { // Handle constraint not being parseable. } v, _ := semver.NewVersion("1.3") if err != nil { // Handle version not being parseable. } // Check if the version meets the constraints. The a variable will be true. a := c.Check(v) Basic Comparisons There are two elements to the comparisons. First, a comparison string is a list of comma separated and comparisons. These are then separated by || separated or comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a comparison that's greater than or equal to 1.2 and less than 3.0.0 or is greater than or equal to 4.2.3. The basic comparisons are: * `=`: equal (aliased to no operator) * `!=`: not equal * `>`: greater than * `<`: less than * `>=`: greater than or equal to * `<=`: less than or equal to Hyphen Range Comparisons There are multiple methods to handle ranges and the first is hyphens ranges. These look like: * `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5` * `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5` Wildcards In Comparisons The `x`, `X`, and `*` characters can be used as a wildcard character. This works for all comparison operators. When used on the `=` operator it falls back to the pack level comparison (see tilde below). For example, * `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` * `>= 1.2.x` is equivalent to `>= 1.2.0` * `<= 2.x` is equivalent to `<= 3` * `*` is equivalent to `>= 0.0.0` Tilde Range Comparisons (Patch) The tilde (`~`) comparison operator is for patch level ranges when a minor version is specified and major level changes when the minor number is missing. For example, * `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0` * `~1` is equivalent to `>= 1, < 2` * `~2.3` is equivalent to `>= 2.3, < 2.4` * `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` * `~1.x` is equivalent to `>= 1, < 2` Caret Range Comparisons (Major) The caret (`^`) comparison operator is for major level changes. This is useful when comparisons of API versions as a major change is API breaking. For example, * `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` * `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` * `^2.3` is equivalent to `>= 2.3, < 3` * `^2.x` is equivalent to `>= 2.0.0, < 3` */ package semver ================================================ FILE: vendor/github.com/Masterminds/semver/version.go ================================================ package semver import ( "bytes" "encoding/json" "errors" "fmt" "regexp" "strconv" "strings" ) // The compiled version of the regex created at init() is cached here so it // only needs to be created once. var versionRegex *regexp.Regexp var validPrereleaseRegex *regexp.Regexp var ( // ErrInvalidSemVer is returned a version is found to be invalid when // being parsed. ErrInvalidSemVer = errors.New("Invalid Semantic Version") // ErrInvalidMetadata is returned when the metadata is an invalid format ErrInvalidMetadata = errors.New("Invalid Metadata string") // ErrInvalidPrerelease is returned when the pre-release is an invalid format ErrInvalidPrerelease = errors.New("Invalid Prerelease string") ) // SemVerRegex is the regular expression used to parse a semantic version. const SemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` + `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` // ValidPrerelease is the regular expression which validates // both prerelease and metadata values. const ValidPrerelease string = `^([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*)` // Version represents a single semantic version. type Version struct { major, minor, patch int64 pre string metadata string original string } func init() { versionRegex = regexp.MustCompile("^" + SemVerRegex + "$") validPrereleaseRegex = regexp.MustCompile(ValidPrerelease) } // NewVersion parses a given version and returns an instance of Version or // an error if unable to parse the version. func NewVersion(v string) (*Version, error) { m := versionRegex.FindStringSubmatch(v) if m == nil { return nil, ErrInvalidSemVer } sv := &Version{ metadata: m[8], pre: m[5], original: v, } var temp int64 temp, err := strconv.ParseInt(m[1], 10, 64) if err != nil { return nil, fmt.Errorf("Error parsing version segment: %s", err) } sv.major = temp if m[2] != "" { temp, err = strconv.ParseInt(strings.TrimPrefix(m[2], "."), 10, 64) if err != nil { return nil, fmt.Errorf("Error parsing version segment: %s", err) } sv.minor = temp } else { sv.minor = 0 } if m[3] != "" { temp, err = strconv.ParseInt(strings.TrimPrefix(m[3], "."), 10, 64) if err != nil { return nil, fmt.Errorf("Error parsing version segment: %s", err) } sv.patch = temp } else { sv.patch = 0 } return sv, nil } // MustParse parses a given version and panics on error. func MustParse(v string) *Version { sv, err := NewVersion(v) if err != nil { panic(err) } return sv } // String converts a Version object to a string. // Note, if the original version contained a leading v this version will not. // See the Original() method to retrieve the original value. Semantic Versions // don't contain a leading v per the spec. Instead it's optional on // impelementation. func (v *Version) String() string { var buf bytes.Buffer fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch) if v.pre != "" { fmt.Fprintf(&buf, "-%s", v.pre) } if v.metadata != "" { fmt.Fprintf(&buf, "+%s", v.metadata) } return buf.String() } // Original returns the original value passed in to be parsed. func (v *Version) Original() string { return v.original } // Major returns the major version. func (v *Version) Major() int64 { return v.major } // Minor returns the minor version. func (v *Version) Minor() int64 { return v.minor } // Patch returns the patch version. func (v *Version) Patch() int64 { return v.patch } // Prerelease returns the pre-release version. func (v *Version) Prerelease() string { return v.pre } // Metadata returns the metadata on the version. func (v *Version) Metadata() string { return v.metadata } // originalVPrefix returns the original 'v' prefix if any. func (v *Version) originalVPrefix() string { // Note, only lowercase v is supported as a prefix by the parser. if v.original != "" && v.original[:1] == "v" { return v.original[:1] } return "" } // IncPatch produces the next patch version. // If the current version does not have prerelease/metadata information, // it unsets metadata and prerelease values, increments patch number. // If the current version has any of prerelease or metadata information, // it unsets both values and keeps curent patch value func (v Version) IncPatch() Version { vNext := v // according to http://semver.org/#spec-item-9 // Pre-release versions have a lower precedence than the associated normal version. // according to http://semver.org/#spec-item-10 // Build metadata SHOULD be ignored when determining version precedence. if v.pre != "" { vNext.metadata = "" vNext.pre = "" } else { vNext.metadata = "" vNext.pre = "" vNext.patch = v.patch + 1 } vNext.original = v.originalVPrefix() + "" + vNext.String() return vNext } // IncMinor produces the next minor version. // Sets patch to 0. // Increments minor number. // Unsets metadata. // Unsets prerelease status. func (v Version) IncMinor() Version { vNext := v vNext.metadata = "" vNext.pre = "" vNext.patch = 0 vNext.minor = v.minor + 1 vNext.original = v.originalVPrefix() + "" + vNext.String() return vNext } // IncMajor produces the next major version. // Sets patch to 0. // Sets minor to 0. // Increments major number. // Unsets metadata. // Unsets prerelease status. func (v Version) IncMajor() Version { vNext := v vNext.metadata = "" vNext.pre = "" vNext.patch = 0 vNext.minor = 0 vNext.major = v.major + 1 vNext.original = v.originalVPrefix() + "" + vNext.String() return vNext } // SetPrerelease defines the prerelease value. // Value must not include the required 'hypen' prefix. func (v Version) SetPrerelease(prerelease string) (Version, error) { vNext := v if len(prerelease) > 0 && !validPrereleaseRegex.MatchString(prerelease) { return vNext, ErrInvalidPrerelease } vNext.pre = prerelease vNext.original = v.originalVPrefix() + "" + vNext.String() return vNext, nil } // SetMetadata defines metadata value. // Value must not include the required 'plus' prefix. func (v Version) SetMetadata(metadata string) (Version, error) { vNext := v if len(metadata) > 0 && !validPrereleaseRegex.MatchString(metadata) { return vNext, ErrInvalidMetadata } vNext.metadata = metadata vNext.original = v.originalVPrefix() + "" + vNext.String() return vNext, nil } // LessThan tests if one version is less than another one. func (v *Version) LessThan(o *Version) bool { return v.Compare(o) < 0 } // GreaterThan tests if one version is greater than another one. func (v *Version) GreaterThan(o *Version) bool { return v.Compare(o) > 0 } // Equal tests if two versions are equal to each other. // Note, versions can be equal with different metadata since metadata // is not considered part of the comparable version. func (v *Version) Equal(o *Version) bool { return v.Compare(o) == 0 } // Compare compares this version to another one. It returns -1, 0, or 1 if // the version smaller, equal, or larger than the other version. // // Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is // lower than the version without a prerelease. func (v *Version) Compare(o *Version) int { // Compare the major, minor, and patch version for differences. If a // difference is found return the comparison. if d := compareSegment(v.Major(), o.Major()); d != 0 { return d } if d := compareSegment(v.Minor(), o.Minor()); d != 0 { return d } if d := compareSegment(v.Patch(), o.Patch()); d != 0 { return d } // At this point the major, minor, and patch versions are the same. ps := v.pre po := o.Prerelease() if ps == "" && po == "" { return 0 } if ps == "" { return 1 } if po == "" { return -1 } return comparePrerelease(ps, po) } // UnmarshalJSON implements JSON.Unmarshaler interface. func (v *Version) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { return err } temp, err := NewVersion(s) if err != nil { return err } v.major = temp.major v.minor = temp.minor v.patch = temp.patch v.pre = temp.pre v.metadata = temp.metadata v.original = temp.original temp = nil return nil } // MarshalJSON implements JSON.Marshaler interface. func (v *Version) MarshalJSON() ([]byte, error) { return json.Marshal(v.String()) } func compareSegment(v, o int64) int { if v < o { return -1 } if v > o { return 1 } return 0 } func comparePrerelease(v, o string) int { // split the prelease versions by their part. The separator, per the spec, // is a . sparts := strings.Split(v, ".") oparts := strings.Split(o, ".") // Find the longer length of the parts to know how many loop iterations to // go through. slen := len(sparts) olen := len(oparts) l := slen if olen > slen { l = olen } // Iterate over each part of the prereleases to compare the differences. for i := 0; i < l; i++ { // Since the lentgh of the parts can be different we need to create // a placeholder. This is to avoid out of bounds issues. stemp := "" if i < slen { stemp = sparts[i] } otemp := "" if i < olen { otemp = oparts[i] } d := comparePrePart(stemp, otemp) if d != 0 { return d } } // Reaching here means two versions are of equal value but have different // metadata (the part following a +). They are not identical in string form // but the version comparison finds them to be equal. return 0 } func comparePrePart(s, o string) int { // Fastpath if they are equal if s == o { return 0 } // When s or o are empty we can use the other in an attempt to determine // the response. if s == "" { if o != "" { return -1 } return 1 } if o == "" { if s != "" { return 1 } return -1 } // When comparing strings "99" is greater than "103". To handle // cases like this we need to detect numbers and compare them. oi, n1 := strconv.ParseInt(o, 10, 64) si, n2 := strconv.ParseInt(s, 10, 64) // The case where both are strings compare the strings if n1 != nil && n2 != nil { if s > o { return 1 } return -1 } else if n1 != nil { // o is a string and s is a number return -1 } else if n2 != nil { // s is a string and o is a number return 1 } // Both are numbers if si > oi { return 1 } return -1 } ================================================ FILE: vendor/github.com/Masterminds/semver/version_test.go ================================================ package semver import ( "encoding/json" "fmt" "testing" ) func TestNewVersion(t *testing.T) { tests := []struct { version string err bool }{ {"1.2.3", false}, {"v1.2.3", false}, {"1.0", false}, {"v1.0", false}, {"1", false}, {"v1", false}, {"1.2.beta", true}, {"v1.2.beta", true}, {"foo", true}, {"1.2-5", false}, {"v1.2-5", false}, {"1.2-beta.5", false}, {"v1.2-beta.5", false}, {"\n1.2", true}, {"\nv1.2", true}, {"1.2.0-x.Y.0+metadata", false}, {"v1.2.0-x.Y.0+metadata", false}, {"1.2.0-x.Y.0+metadata-width-hypen", false}, {"v1.2.0-x.Y.0+metadata-width-hypen", false}, {"1.2.3-rc1-with-hypen", false}, {"v1.2.3-rc1-with-hypen", false}, {"1.2.3.4", true}, {"v1.2.3.4", true}, {"1.2.2147483648", false}, {"1.2147483648.3", false}, {"2147483648.3.0", false}, } for _, tc := range tests { _, err := NewVersion(tc.version) if tc.err && err == nil { t.Fatalf("expected error for version: %s", tc.version) } else if !tc.err && err != nil { t.Fatalf("error for version %s: %s", tc.version, err) } } } func TestOriginal(t *testing.T) { tests := []string{ "1.2.3", "v1.2.3", "1.0", "v1.0", "1", "v1", "1.2-5", "v1.2-5", "1.2-beta.5", "v1.2-beta.5", "1.2.0-x.Y.0+metadata", "v1.2.0-x.Y.0+metadata", "1.2.0-x.Y.0+metadata-width-hypen", "v1.2.0-x.Y.0+metadata-width-hypen", "1.2.3-rc1-with-hypen", "v1.2.3-rc1-with-hypen", } for _, tc := range tests { v, err := NewVersion(tc) if err != nil { t.Errorf("Error parsing version %s", tc) } o := v.Original() if o != tc { t.Errorf("Error retrieving originl. Expected '%s' but got '%s'", tc, v) } } } func TestParts(t *testing.T) { v, err := NewVersion("1.2.3-beta.1+build.123") if err != nil { t.Error("Error parsing version 1.2.3-beta.1+build.123") } if v.Major() != 1 { t.Error("Major() returning wrong value") } if v.Minor() != 2 { t.Error("Minor() returning wrong value") } if v.Patch() != 3 { t.Error("Patch() returning wrong value") } if v.Prerelease() != "beta.1" { t.Error("Prerelease() returning wrong value") } if v.Metadata() != "build.123" { t.Error("Metadata() returning wrong value") } } func TestString(t *testing.T) { tests := []struct { version string expected string }{ {"1.2.3", "1.2.3"}, {"v1.2.3", "1.2.3"}, {"1.0", "1.0.0"}, {"v1.0", "1.0.0"}, {"1", "1.0.0"}, {"v1", "1.0.0"}, {"1.2-5", "1.2.0-5"}, {"v1.2-5", "1.2.0-5"}, {"1.2-beta.5", "1.2.0-beta.5"}, {"v1.2-beta.5", "1.2.0-beta.5"}, {"1.2.0-x.Y.0+metadata", "1.2.0-x.Y.0+metadata"}, {"v1.2.0-x.Y.0+metadata", "1.2.0-x.Y.0+metadata"}, {"1.2.0-x.Y.0+metadata-width-hypen", "1.2.0-x.Y.0+metadata-width-hypen"}, {"v1.2.0-x.Y.0+metadata-width-hypen", "1.2.0-x.Y.0+metadata-width-hypen"}, {"1.2.3-rc1-with-hypen", "1.2.3-rc1-with-hypen"}, {"v1.2.3-rc1-with-hypen", "1.2.3-rc1-with-hypen"}, } for _, tc := range tests { v, err := NewVersion(tc.version) if err != nil { t.Errorf("Error parsing version %s", tc) } s := v.String() if s != tc.expected { t.Errorf("Error generating string. Expected '%s' but got '%s'", tc.expected, s) } } } func TestCompare(t *testing.T) { tests := []struct { v1 string v2 string expected int }{ {"1.2.3", "1.5.1", -1}, {"2.2.3", "1.5.1", 1}, {"2.2.3", "2.2.2", 1}, {"3.2-beta", "3.2-beta", 0}, {"1.3", "1.1.4", 1}, {"4.2", "4.2-beta", 1}, {"4.2-beta", "4.2", -1}, {"4.2-alpha", "4.2-beta", -1}, {"4.2-alpha", "4.2-alpha", 0}, {"4.2-beta.2", "4.2-beta.1", 1}, {"4.2-beta2", "4.2-beta1", 1}, {"4.2-beta", "4.2-beta.2", -1}, {"4.2-beta", "4.2-beta.foo", -1}, {"4.2-beta.2", "4.2-beta", 1}, {"4.2-beta.foo", "4.2-beta", 1}, {"1.2+bar", "1.2+baz", 0}, } for _, tc := range tests { v1, err := NewVersion(tc.v1) if err != nil { t.Errorf("Error parsing version: %s", err) } v2, err := NewVersion(tc.v2) if err != nil { t.Errorf("Error parsing version: %s", err) } a := v1.Compare(v2) e := tc.expected if a != e { t.Errorf( "Comparison of '%s' and '%s' failed. Expected '%d', got '%d'", tc.v1, tc.v2, e, a, ) } } } func TestLessThan(t *testing.T) { tests := []struct { v1 string v2 string expected bool }{ {"1.2.3", "1.5.1", true}, {"2.2.3", "1.5.1", false}, {"3.2-beta", "3.2-beta", false}, } for _, tc := range tests { v1, err := NewVersion(tc.v1) if err != nil { t.Errorf("Error parsing version: %s", err) } v2, err := NewVersion(tc.v2) if err != nil { t.Errorf("Error parsing version: %s", err) } a := v1.LessThan(v2) e := tc.expected if a != e { t.Errorf( "Comparison of '%s' and '%s' failed. Expected '%t', got '%t'", tc.v1, tc.v2, e, a, ) } } } func TestGreaterThan(t *testing.T) { tests := []struct { v1 string v2 string expected bool }{ {"1.2.3", "1.5.1", false}, {"2.2.3", "1.5.1", true}, {"3.2-beta", "3.2-beta", false}, {"3.2.0-beta.1", "3.2.0-beta.5", false}, {"3.2-beta.4", "3.2-beta.2", true}, {"7.43.0-SNAPSHOT.99", "7.43.0-SNAPSHOT.103", false}, {"7.43.0-SNAPSHOT.FOO", "7.43.0-SNAPSHOT.103", true}, {"7.43.0-SNAPSHOT.99", "7.43.0-SNAPSHOT.BAR", false}, } for _, tc := range tests { v1, err := NewVersion(tc.v1) if err != nil { t.Errorf("Error parsing version: %s", err) } v2, err := NewVersion(tc.v2) if err != nil { t.Errorf("Error parsing version: %s", err) } a := v1.GreaterThan(v2) e := tc.expected if a != e { t.Errorf( "Comparison of '%s' and '%s' failed. Expected '%t', got '%t'", tc.v1, tc.v2, e, a, ) } } } func TestEqual(t *testing.T) { tests := []struct { v1 string v2 string expected bool }{ {"1.2.3", "1.5.1", false}, {"2.2.3", "1.5.1", false}, {"3.2-beta", "3.2-beta", true}, {"3.2-beta+foo", "3.2-beta+bar", true}, } for _, tc := range tests { v1, err := NewVersion(tc.v1) if err != nil { t.Errorf("Error parsing version: %s", err) } v2, err := NewVersion(tc.v2) if err != nil { t.Errorf("Error parsing version: %s", err) } a := v1.Equal(v2) e := tc.expected if a != e { t.Errorf( "Comparison of '%s' and '%s' failed. Expected '%t', got '%t'", tc.v1, tc.v2, e, a, ) } } } func TestInc(t *testing.T) { tests := []struct { v1 string expected string how string expectedOriginal string }{ {"1.2.3", "1.2.4", "patch", "1.2.4"}, {"v1.2.4", "1.2.5", "patch", "v1.2.5"}, {"1.2.3", "1.3.0", "minor", "1.3.0"}, {"v1.2.4", "1.3.0", "minor", "v1.3.0"}, {"1.2.3", "2.0.0", "major", "2.0.0"}, {"v1.2.4", "2.0.0", "major", "v2.0.0"}, {"1.2.3+meta", "1.2.4", "patch", "1.2.4"}, {"1.2.3-beta+meta", "1.2.3", "patch", "1.2.3"}, {"v1.2.4-beta+meta", "1.2.4", "patch", "v1.2.4"}, {"1.2.3-beta+meta", "1.3.0", "minor", "1.3.0"}, {"v1.2.4-beta+meta", "1.3.0", "minor", "v1.3.0"}, {"1.2.3-beta+meta", "2.0.0", "major", "2.0.0"}, {"v1.2.4-beta+meta", "2.0.0", "major", "v2.0.0"}, } for _, tc := range tests { v1, err := NewVersion(tc.v1) if err != nil { t.Errorf("Error parsing version: %s", err) } var v2 Version switch tc.how { case "patch": v2 = v1.IncPatch() case "minor": v2 = v1.IncMinor() case "major": v2 = v1.IncMajor() } a := v2.String() e := tc.expected if a != e { t.Errorf( "Inc %q failed. Expected %q got %q", tc.how, e, a, ) } a = v2.Original() e = tc.expectedOriginal if a != e { t.Errorf( "Inc %q failed. Expected original %q got %q", tc.how, e, a, ) } } } func TestSetPrerelease(t *testing.T) { tests := []struct { v1 string prerelease string expectedVersion string expectedPrerelease string expectedOriginal string expectedErr error }{ {"1.2.3", "**", "1.2.3", "", "1.2.3", ErrInvalidPrerelease}, {"1.2.3", "beta", "1.2.3-beta", "beta", "1.2.3-beta", nil}, {"v1.2.4", "beta", "1.2.4-beta", "beta", "v1.2.4-beta", nil}, } for _, tc := range tests { v1, err := NewVersion(tc.v1) if err != nil { t.Errorf("Error parsing version: %s", err) } v2, err := v1.SetPrerelease(tc.prerelease) if err != tc.expectedErr { t.Errorf("Expected to get err=%s, but got err=%s", tc.expectedErr, err) } a := v2.Prerelease() e := tc.expectedPrerelease if a != e { t.Errorf("Expected prerelease value=%q, but got %q", e, a) } a = v2.String() e = tc.expectedVersion if a != e { t.Errorf("Expected version string=%q, but got %q", e, a) } a = v2.Original() e = tc.expectedOriginal if a != e { t.Errorf("Expected version original=%q, but got %q", e, a) } } } func TestSetMetadata(t *testing.T) { tests := []struct { v1 string metadata string expectedVersion string expectedMetadata string expectedOriginal string expectedErr error }{ {"1.2.3", "**", "1.2.3", "", "1.2.3", ErrInvalidMetadata}, {"1.2.3", "meta", "1.2.3+meta", "meta", "1.2.3+meta", nil}, {"v1.2.4", "meta", "1.2.4+meta", "meta", "v1.2.4+meta", nil}, } for _, tc := range tests { v1, err := NewVersion(tc.v1) if err != nil { t.Errorf("Error parsing version: %s", err) } v2, err := v1.SetMetadata(tc.metadata) if err != tc.expectedErr { t.Errorf("Expected to get err=%s, but got err=%s", tc.expectedErr, err) } a := v2.Metadata() e := tc.expectedMetadata if a != e { t.Errorf("Expected metadata value=%q, but got %q", e, a) } a = v2.String() e = tc.expectedVersion if e != a { t.Errorf("Expected version string=%q, but got %q", e, a) } a = v2.Original() e = tc.expectedOriginal if a != e { t.Errorf("Expected version original=%q, but got %q", e, a) } } } func TestOriginalVPrefix(t *testing.T) { tests := []struct { version string vprefix string }{ {"1.2.3", ""}, {"v1.2.4", "v"}, } for _, tc := range tests { v1, _ := NewVersion(tc.version) a := v1.originalVPrefix() e := tc.vprefix if a != e { t.Errorf("Expected vprefix=%q, but got %q", e, a) } } } func TestJsonMarshal(t *testing.T) { sVer := "1.1.1" x, err := NewVersion(sVer) if err != nil { t.Errorf("Error creating version: %s", err) } out, err2 := json.Marshal(x) if err2 != nil { t.Errorf("Error marshaling version: %s", err2) } got := string(out) want := fmt.Sprintf("%q", sVer) if got != want { t.Errorf("Error marshaling unexpected marshaled content: got=%q want=%q", got, want) } } func TestJsonUnmarshal(t *testing.T) { sVer := "1.1.1" ver := &Version{} err := json.Unmarshal([]byte(fmt.Sprintf("%q", sVer)), ver) if err != nil { t.Errorf("Error unmarshaling version: %s", err) } got := ver.String() want := sVer if got != want { t.Errorf("Error unmarshaling unexpected object content: got=%q want=%q", got, want) } } ================================================ FILE: vendor/github.com/Masterminds/vcs/.gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof ================================================ FILE: vendor/github.com/Masterminds/vcs/.travis.yml ================================================ language: go dist: xenial go: - 1.6.x - 1.7.x - 1.8.x - 1.9.x - 1.10.x - 1.11.x - 1.12.x - master before_script: - git version - svn --version # Need a more up to date verion of mercurial to handle TLS with # bitbucket properly. Also need python greater than 2.7.9. - pyenv versions && pyenv rehash && pyenv versions - pyenv global 2.7.15 - openssl ciphers -v | awk '{print $2}' | sort | uniq - sudo pip install mercurial --upgrade # The below is a complete hack to have hg use the pyenv version of python - sudo sed -i '1s/.*/\#\!\/usr\/bin\/env\ python/' /usr/local/bin/hg - hg --version # Setting sudo access to false will let Travis CI use containers rather than # VMs to run the tests. For more details see: # - http://docs.travis-ci.com/user/workers/container-based-infrastructure/ # - http://docs.travis-ci.com/user/workers/standard-infrastructure/ sudo: false script: - make setup - make test notifications: webhooks: urls: - https://webhooks.gitter.im/e/06e3328629952dabe3e0 on_success: change # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always on_start: never # options: [always|never|change] default: always ================================================ FILE: vendor/github.com/Masterminds/vcs/CHANGELOG.md ================================================ # Changelog ## 1.13.1 (2019-07-09) ### Fixed - #101: Updated bitbucket API call as previous API was removed - #97: Fixed travis ci building - #95: Fixed "git clean" invocation for submodule ## 1.13.0 (2019-02-27) ### Changed - #92: Allow non-200 remote lookup responses for Go style redirects ### Fixed - #91: For Mercurial/Hg return an error if Version() called and Hg prints to stderr - #87 and #93: Fix CI issues ## 1.12.0 (2017-09-11) ### Changed - #79: Include the error context in the error string (thanks @guywithnose) - #80: Bump the Go versions for Travis CI testing (thanks @AlekSi) ## 1.11.1 (2017-04-28) ### Fixed - #76: Fix submodule handling for Windows (thanks @m0j0hn) ## 1.11.0 (2017-03-23) ### Added - #65: Exposed CmdFromDir function (thanks @erizocosmico) ### Changed - #69: Updated testing for Go 1.8 ### Fixed - #64: Testing fatal error if bzr not installed (thanks @kevinburke) ## 1.10.2 (2017-01-24) ### Fixed - #63: Remove extra quotes in submodule export (thanks @dt) ## 1.10.1 (2017-01-18) ### Fixed - #62: Added windows testing via appveyor and fixed issues under windows. ## 1.10.0 (2017-01-09) ### Added - #60: Handle Git submodules (thanks @sdboyer) - #61: Add gometalinter to testing ## 1.9.0 (2016-11-18) ### Added - #50: Auto-detect remotes with file:// prefix. - #59: Testing against Go 1.7 ### Changed - Removed auto-detection for Google Code as the service is deprecated - Added auto-detection of git.openstack.org ### Fixed - #53: Git not fetching tags off branch ## 1.8.0 (2016-06-29) ### Added - #43: Detect when tool (e.g., git, svn, etc) not installed - #49: Detect access denied and not found situations ### Changed - #48: Updated Go Report Gard url to new format - Refactored SVN handling to detect when not in a top level directory - Updating tagging to v[SemVer] structure for compatibility with other tools. ### Fixed - #45: Fixed hg's update method so that it pulls from remote before updates ## 1.7.0 (2016-05-05) - Adds a glide.yaml file with some limited information. - Implements #37: Ability to export source as a directory. - Implements #36: Get current version-ish with Current method. This returns a branch (if on tip) or equivalent tip, a tag if on a tag, or a revision if on an individual revision. Note, the tip of branch is VCS specific so usage may require detecting VCS type. ## 1.6.1 (2016-04-27) - Fixed #30: tags from commit should not have ^{} appended (seen in git) - Fixed #29: isDetachedHead fails with non-english locales (git) - Fixed #33: Access denied and not found http errors causing xml parsing errors ## 1.6.0 (2016-04-18) - Issue #26: Added Init method to initialize a repo at the local location (thanks tony). - Issue #19: Added method to retrieve tags for a commit. - Issue #24: Reworked errors returned from common methods. Now differing VCS implementations return the same errors. The original VCS specific error is available on the error. See the docs for more details. - Issue #25: Export the function RunFromDir which runs VCS commands from the root of the local directory. This is useful for those that want to build and extend on top of the vcs package (thanks tony). - Issue #22: Added Ping command to test if remote location is present and accessible. ## 1.5.1 (2016-03-23) - Fixing bug parsing some Git commit dates. ## 1.5.0 (2016-03-22) - Add Travis CI testing for Go 1.6. - Issue #17: Add CommitInfo method allowing for a common way to get commit metadata from all VCS. - Autodetect types that have git@ or hg@ users. - Autodetect git+ssh, bzr+ssh, git, and svn+ssh scheme urls. - On Bitbucket for ssh style URLs retrieve the type from the URL. This allows for private repo type detection. - Issue #14: Autodetect ssh/scp style urls (thanks chonthu). ## 1.4.1 (2016-03-07) - Fixes #16: some windows situations are unable to create parent directory. ## 1.4.0 (2016-02-15) - Adding support for IBM JazzHub. ## 1.3.1 (2016-01-27) - Issue #12: Failed to checkout Bzr repo when parent directory didn't exist (thanks cyrilleverrier). ## 1.3.0 (2015-11-09) - Issue #9: Added Date method to get the date/time of latest commit (thanks kamilchm). ## 1.2.0 (2015-10-29) - Adding IsDirty method to detect a checkout with uncommitted changes. ## 1.1.4 (2015-10-28) - Fixed #8: Git IsReference not detecting branches that have not been checked out yet. ## 1.1.3 (2015-10-21) - Fixing issue where there are multiple go-import statements for go redirects ## 1.1.2 (2015-10-20) - Fixes #7: hg not checking out code when Get is called ## 1.1.1 (2015-10-20) - Issue #6: Allow VCS commands to be run concurrently. ## 1.1.0 (2015-10-19) - #5: Added output of failed command to returned errors. ## 1.0.0 (2015-10-06) - Initial release. ================================================ FILE: vendor/github.com/Masterminds/vcs/LICENSE.txt ================================================ The Masterminds Copyright (C) 2014-2015, Matt Butcher and Matt Farina 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. ================================================ FILE: vendor/github.com/Masterminds/vcs/Makefile ================================================ .PHONY: setup setup: go get -u gopkg.in/alecthomas/gometalinter.v1 gometalinter.v1 --install .PHONY: test test: validate lint @echo "==> Running tests" go test -v .PHONY: validate validate: # misspell finds the work adresář (used in bzr.go) as a mispelling of # address. It finds adres. An issue has been filed at # https://github.com/client9/misspell/issues/99. In the meantime adding # adres to the ignore list. @echo "==> Running static validations" @gometalinter.v1 \ --disable-all \ --linter "misspell:misspell -i adres -j 1 {path}/*.go:PATH:LINE:COL:MESSAGE" \ --enable deadcode \ --severity deadcode:error \ --enable gofmt \ --enable gosimple \ --enable ineffassign \ --enable misspell \ --enable vet \ --tests \ --vendor \ --deadline 60s \ ./... || exit_code=1 .PHONY: lint lint: @echo "==> Running linters" @gometalinter.v1 \ --disable-all \ --enable golint \ --vendor \ --deadline 60s \ ./... || : ================================================ FILE: vendor/github.com/Masterminds/vcs/README.md ================================================ # VCS Repository Management for Go Manage repos in varying version control systems with ease through a common interface. [![Build Status](https://travis-ci.org/Masterminds/vcs.svg)](https://travis-ci.org/Masterminds/vcs) [![GoDoc](https://godoc.org/github.com/Masterminds/vcs?status.png)](https://godoc.org/github.com/Masterminds/vcs) [![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/vcs)](https://goreportcard.com/report/github.com/Masterminds/vcs) [![Build status](https://ci.appveyor.com/api/projects/status/vg3cjc561q2trobm?svg=true&passingText=windows%20build%20passing&failingText=windows%20build%20failing)](https://ci.appveyor.com/project/mattfarina/vcs) ## Quick Usage Quick usage: remote := "https://github.com/Masterminds/vcs" local, _ := ioutil.TempDir("", "go-vcs") repo, err := NewRepo(remote, local) In this case `NewRepo` will detect the VCS is Git and return a `GitRepo`. All of the repos implement the `Repo` interface with a common set of features between them. ## Supported VCS Git, SVN, Bazaar (Bzr), and Mercurial (Hg) are currently supported. They each have their own type (e.g., `GitRepo`) that follow a simple naming pattern. Each type implements the `Repo` interface and has a constructor (e.g., `NewGitRepo`). The constructors have the same signature as `NewRepo`. ## Features - Clone or checkout a repository depending on the version control system. - Pull updates to a repository. - Get the currently checked out commit id. - Checkout a commit id, branch, or tag (depending on the availability in the VCS). - Get a list of tags and branches in the VCS. - Check if a string value is a valid reference within the VCS. - More... For more details see [the documentation](https://godoc.org/github.com/Masterminds/vcs). ## Motivation The package `golang.org/x/tools/go/vcs` provides some valuable functionality for working with packages in repositories in varying source control management systems. That package, while useful and well tested, is designed with a specific purpose in mind. Our uses went beyond the scope of that package. To implement our scope we built a package that went beyond the functionality and scope of `golang.org/x/tools/go/vcs`. ================================================ FILE: vendor/github.com/Masterminds/vcs/appveyor.yml ================================================ version: build-{build}.{branch} clone_folder: C:\gopath\src\github.com\Masterminds\vcs shallow_clone: true environment: GOPATH: C:\gopath platform: - x64 install: - go version - go env - choco install -y bzr - set PATH=C:\Program Files (x86)\Bazaar;%PATH% - bzr --version build_script: - go install -v ./... test_script: - go test -v deploy: off ================================================ FILE: vendor/github.com/Masterminds/vcs/bzr.go ================================================ package vcs import ( "fmt" "net/url" "os" "os/exec" "path/filepath" "regexp" "strings" "time" ) var bzrDetectURL = regexp.MustCompile("parent branch: (?P.+)\n") // NewBzrRepo creates a new instance of BzrRepo. The remote and local directories // need to be passed in. func NewBzrRepo(remote, local string) (*BzrRepo, error) { ins := depInstalled("bzr") if !ins { return nil, NewLocalError("bzr is not installed", nil, "") } ltype, err := DetectVcsFromFS(local) // Found a VCS other than Bzr. Need to report an error. if err == nil && ltype != Bzr { return nil, ErrWrongVCS } r := &BzrRepo{} r.setRemote(remote) r.setLocalPath(local) r.Logger = Logger // With the other VCS we can check if the endpoint locally is different // from the one configured internally. But, with Bzr you can't. For example, // if you do `bzr branch https://launchpad.net/govcstestbzrrepo` and then // use `bzr info` to get the parent branch you'll find it set to // http://bazaar.launchpad.net/~mattfarina/govcstestbzrrepo/trunk/. Notice // the change from https to http and the path chance. // Here we set the remote to be the local one if none is passed in. if err == nil && r.CheckLocal() && remote == "" { c := exec.Command("bzr", "info") c.Dir = local c.Env = envForDir(c.Dir) out, err := c.CombinedOutput() if err != nil { return nil, NewLocalError("Unable to retrieve local repo information", err, string(out)) } m := bzrDetectURL.FindStringSubmatch(string(out)) // If no remote was passed in but one is configured for the locally // checked out Bzr repo use that one. if m[1] != "" { r.setRemote(m[1]) } } return r, nil } // BzrRepo implements the Repo interface for the Bzr source control. type BzrRepo struct { base } // Vcs retrieves the underlying VCS being implemented. func (s BzrRepo) Vcs() Type { return Bzr } // Get is used to perform an initial clone of a repository. func (s *BzrRepo) Get() error { basePath := filepath.Dir(filepath.FromSlash(s.LocalPath())) if _, err := os.Stat(basePath); os.IsNotExist(err) { err = os.MkdirAll(basePath, 0755) if err != nil { return NewLocalError("Unable to create directory", err, "") } } out, err := s.run("bzr", "branch", s.Remote(), s.LocalPath()) if err != nil { return NewRemoteError("Unable to get repository", err, string(out)) } return nil } // Init initializes a bazaar repository at local location. func (s *BzrRepo) Init() error { out, err := s.run("bzr", "init", s.LocalPath()) // There are some windows cases where bazaar cannot create the parent // directory if it does not already exist, to the location it's trying // to create the repo. Catch that error and try to handle it. if err != nil && s.isUnableToCreateDir(err) { basePath := filepath.Dir(filepath.FromSlash(s.LocalPath())) if _, err := os.Stat(basePath); os.IsNotExist(err) { err = os.MkdirAll(basePath, 0755) if err != nil { return NewLocalError("Unable to initialize repository", err, "") } out, err = s.run("bzr", "init", s.LocalPath()) if err != nil { return NewLocalError("Unable to initialize repository", err, string(out)) } return nil } } else if err != nil { return NewLocalError("Unable to initialize repository", err, string(out)) } return nil } // Update performs a Bzr pull and update to an existing checkout. func (s *BzrRepo) Update() error { out, err := s.RunFromDir("bzr", "pull") if err != nil { return NewRemoteError("Unable to update repository", err, string(out)) } out, err = s.RunFromDir("bzr", "update") if err != nil { return NewRemoteError("Unable to update repository", err, string(out)) } return nil } // UpdateVersion sets the version of a package currently checked out via Bzr. func (s *BzrRepo) UpdateVersion(version string) error { out, err := s.RunFromDir("bzr", "update", "-r", version) if err != nil { return NewLocalError("Unable to update checked out version", err, string(out)) } return nil } // Version retrieves the current version. func (s *BzrRepo) Version() (string, error) { out, err := s.RunFromDir("bzr", "revno", "--tree") if err != nil { return "", NewLocalError("Unable to retrieve checked out version", err, string(out)) } return strings.TrimSpace(string(out)), nil } // Current returns the current version-ish. This means: // * -1 if on the tip of the branch (this is the Bzr value for HEAD) // * A tag if on a tag // * Otherwise a revision func (s *BzrRepo) Current() (string, error) { tip, err := s.CommitInfo("-1") if err != nil { return "", err } curr, err := s.Version() if err != nil { return "", err } if tip.Commit == curr { return "-1", nil } ts, err := s.TagsFromCommit(curr) if err != nil { return "", err } if len(ts) > 0 { return ts[0], nil } return curr, nil } // Date retrieves the date on the latest commit. func (s *BzrRepo) Date() (time.Time, error) { out, err := s.RunFromDir("bzr", "version-info", "--custom", "--template={date}") if err != nil { return time.Time{}, NewLocalError("Unable to retrieve revision date", err, string(out)) } t, err := time.Parse(longForm, string(out)) if err != nil { return time.Time{}, NewLocalError("Unable to retrieve revision date", err, string(out)) } return t, nil } // CheckLocal verifies the local location is a Bzr repo. func (s *BzrRepo) CheckLocal() bool { if _, err := os.Stat(s.LocalPath() + "/.bzr"); err == nil { return true } return false } // Branches returns a list of available branches on the repository. // In Bazaar (Bzr) clones and branches are the same. A different branch will // have a different URL location which we cannot detect from the repo. This // is a little different from other VCS. func (s *BzrRepo) Branches() ([]string, error) { var branches []string return branches, nil } // Tags returns a list of available tags on the repository. func (s *BzrRepo) Tags() ([]string, error) { out, err := s.RunFromDir("bzr", "tags") if err != nil { return []string{}, NewLocalError("Unable to retrieve tags", err, string(out)) } tags := s.referenceList(string(out), `(?m-s)^(\S+)`) return tags, nil } // IsReference returns if a string is a reference. A reference can be a // commit id or tag. func (s *BzrRepo) IsReference(r string) bool { _, err := s.RunFromDir("bzr", "revno", "-r", r) return err == nil } // IsDirty returns if the checkout has been modified from the checked // out reference. func (s *BzrRepo) IsDirty() bool { out, err := s.RunFromDir("bzr", "diff") return err != nil || len(out) != 0 } // CommitInfo retrieves metadata about a commit. func (s *BzrRepo) CommitInfo(id string) (*CommitInfo, error) { r := "-r" + id out, err := s.RunFromDir("bzr", "log", r, "--log-format=long") if err != nil { return nil, ErrRevisionUnavailable } ci := &CommitInfo{} lines := strings.Split(string(out), "\n") const format = "Mon 2006-01-02 15:04:05 -0700" var track int var trackOn bool // Note, bzr does not appear to use i18m. for i, l := range lines { if strings.HasPrefix(l, "revno:") { ci.Commit = strings.TrimSpace(strings.TrimPrefix(l, "revno:")) } else if strings.HasPrefix(l, "committer:") { ci.Author = strings.TrimSpace(strings.TrimPrefix(l, "committer:")) } else if strings.HasPrefix(l, "timestamp:") { ts := strings.TrimSpace(strings.TrimPrefix(l, "timestamp:")) ci.Date, err = time.Parse(format, ts) if err != nil { return nil, NewLocalError("Unable to retrieve commit information", err, string(out)) } } else if strings.TrimSpace(l) == "message:" { track = i trackOn = true } else if trackOn && i > track { ci.Message = ci.Message + l } } ci.Message = strings.TrimSpace(ci.Message) // Didn't find the revision if ci.Author == "" { return nil, ErrRevisionUnavailable } return ci, nil } // TagsFromCommit retrieves tags from a commit id. func (s *BzrRepo) TagsFromCommit(id string) ([]string, error) { out, err := s.RunFromDir("bzr", "tags", "-r", id) if err != nil { return []string{}, NewLocalError("Unable to retrieve tags", err, string(out)) } tags := s.referenceList(string(out), `(?m-s)^(\S+)`) return tags, nil } // Ping returns if remote location is accessible. func (s *BzrRepo) Ping() bool { // Running bzr info is slow. Many of the projects are on launchpad which // has a public 1.0 API we can use. u, err := url.Parse(s.Remote()) if err == nil { if u.Host == "launchpad.net" { try := strings.TrimPrefix(u.Path, "/") // get returns the body and an err. If the status code is not a 200 // an error is returned. Launchpad returns a 404 for a codebase that // does not exist. Otherwise it returns a JSON object describing it. _, er := get("https://api.launchpad.net/1.0/" + try) return er == nil } } // This is the same command that Go itself uses but it's not fast (or fast // enough by my standards). A faster method would be useful. _, err = s.run("bzr", "info", s.Remote()) return err == nil } // ExportDir exports the current revision to the passed in directory. func (s *BzrRepo) ExportDir(dir string) error { out, err := s.RunFromDir("bzr", "export", dir) s.log(out) if err != nil { return NewLocalError("Unable to export source", err, string(out)) } return nil } // Multi-lingual manner check for the VCS error that it couldn't create directory. // https://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev/files/head:/po/ func (s *BzrRepo) isUnableToCreateDir(err error) bool { msg := err.Error() if strings.HasPrefix(msg, fmt.Sprintf("Parent directory of %s does not exist.", s.LocalPath())) || strings.HasPrefix(msg, fmt.Sprintf("Nadřazený adresář %s neexistuje.", s.LocalPath())) || strings.HasPrefix(msg, fmt.Sprintf("El directorio padre de %s no existe.", s.LocalPath())) || strings.HasPrefix(msg, fmt.Sprintf("%s の親ディレクトリがありません。", s.LocalPath())) || strings.HasPrefix(msg, fmt.Sprintf("Родительская директория для %s не существует.", s.LocalPath())) { return true } return false } ================================================ FILE: vendor/github.com/Masterminds/vcs/bzr_test.go ================================================ package vcs import ( "io/ioutil" "path/filepath" "time" //"log" "os" "testing" ) // Canary test to ensure BzrRepo implements the Repo interface. var _ Repo = &BzrRepo{} // To verify bzr is working we perform integration testing // with a known bzr service. Due to the long time of repeatedly checking out // repos these tests are structured to work together. func TestBzr(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-bzr-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewBzrRepo("https://launchpad.net/govcstestbzrrepo", tempDir+"/govcstestbzrrepo") if err != nil { t.Fatal(err) } if repo.Vcs() != Bzr { t.Error("Bzr is detecting the wrong type") } // Check the basic getters. if repo.Remote() != "https://launchpad.net/govcstestbzrrepo" { t.Error("Remote not set properly") } if repo.LocalPath() != tempDir+"/govcstestbzrrepo" { t.Error("Local disk location not set properly") } //Logger = log.New(os.Stdout, "", log.LstdFlags) // Do an initial clone. err = repo.Get() if err != nil { t.Errorf("Unable to clone Bzr repo. Err was %s", err) } // Verify Bzr repo is a Bzr repo if !repo.CheckLocal() { t.Error("Problem checking out repo or Bzr CheckLocal is not working") } // Test internal lookup mechanism used outside of Bzr specific functionality. ltype, err := DetectVcsFromFS(tempDir + "/govcstestbzrrepo") if err != nil { t.Error("detectVcsFromFS unable to Bzr repo") } if ltype != Bzr { t.Errorf("detectVcsFromFS detected %s instead of Bzr type", ltype) } // Test NewRepo on existing checkout. This should simply provide a working // instance without error based on looking at the local directory. nrepo, nrerr := NewRepo("https://launchpad.net/govcstestbzrrepo", tempDir+"/govcstestbzrrepo") if nrerr != nil { t.Error(nrerr) } // Verify the right oject is returned. It will check the local repo type. if !nrepo.CheckLocal() { t.Error("Wrong version returned from NewRepo") } v, err := repo.Current() if err != nil { t.Errorf("Error trying Bzr Current: %s", err) } if v != "-1" { t.Errorf("Current failed to detect Bzr on tip of branch. Got version: %s", v) } err = repo.UpdateVersion("2") if err != nil { t.Errorf("Unable to update Bzr repo version. Err was %s", err) } // Use Version to verify we are on the right version. v, err = repo.Version() if v != "2" { t.Error("Error checking checked out Bzr version") } if err != nil { t.Error(err) } v, err = repo.Current() if err != nil { t.Errorf("Error trying Bzr Current: %s", err) } if v != "2" { t.Errorf("Current failed to detect Bzr on rev 2 of branch. Got version: %s", v) } // Use Date to verify we are on the right commit. d, err := repo.Date() if d.Format(longForm) != "2015-07-31 09:50:42 -0400" { t.Error("Error checking checked out Bzr commit date") } if err != nil { t.Error(err) } // Perform an update. err = repo.Update() if err != nil { t.Error(err) } v, err = repo.Version() if v != "3" { t.Error("Error checking checked out Bzr version") } if err != nil { t.Error(err) } tags, err := repo.Tags() if err != nil { t.Error(err) } if tags[0] != "1.0.0" { t.Error("Bzr tags is not reporting the correct version") } tags, err = repo.TagsFromCommit("2") if err != nil { t.Error(err) } if len(tags) != 0 { t.Error("Bzr is incorrectly returning tags for a commit") } tags, err = repo.TagsFromCommit("3") if err != nil { t.Error(err) } if len(tags) != 1 || tags[0] != "1.0.0" { t.Error("Bzr is incorrectly returning tags for a commit") } branches, err := repo.Branches() if err != nil { t.Error(err) } if len(branches) != 0 { t.Error("Bzr is incorrectly returning branches") } if !repo.IsReference("1.0.0") { t.Error("Bzr is reporting a reference is not one") } if repo.IsReference("foo") { t.Error("Bzr is reporting a non-existent reference is one") } if repo.IsDirty() { t.Error("Bzr incorrectly reporting dirty") } ci, err := repo.CommitInfo("3") if err != nil { t.Error(err) } if ci.Commit != "3" { t.Error("Bzr.CommitInfo wrong commit id") } if ci.Author != "Matt Farina " { t.Error("Bzr.CommitInfo wrong author") } if ci.Message != "Updated Readme with pointer." { t.Error("Bzr.CommitInfo wrong message") } ti, err := time.Parse(time.RFC1123Z, "Fri, 31 Jul 2015 09:51:37 -0400") if err != nil { t.Error(err) } if !ti.Equal(ci.Date) { t.Error("Bzr.CommitInfo wrong date") } _, err = repo.CommitInfo("asdfasdfasdf") if err != ErrRevisionUnavailable { t.Error("Bzr didn't return expected ErrRevisionUnavailable") } tempDir2, err := ioutil.TempDir("", "go-vcs-bzr-tests-export") if err != nil { t.Fatalf("Error creating temp directory: %s", err) } defer func() { err = os.RemoveAll(tempDir2) if err != nil { t.Error(err) } }() exportDir := filepath.Join(tempDir2, "src") err = repo.ExportDir(exportDir) if err != nil { t.Errorf("Unable to export Bzr repo. Err was %s", err) } _, err = os.Stat(filepath.Join(exportDir, "Readme.md")) if err != nil { t.Errorf("Error checking exported file in Bzr: %s", err) } _, err = os.Stat(filepath.Join(exportDir, string(repo.Vcs()))) if err != nil { if found := os.IsNotExist(err); !found { t.Errorf("Error checking exported metadata in Bzr: %s", err) } } else { t.Error("Error checking Bzr metadata. It exists.") } } func TestBzrCheckLocal(t *testing.T) { // Verify repo.CheckLocal fails for non-Bzr directories. // TestBzr is already checking on a valid repo tempDir, err := ioutil.TempDir("", "go-vcs-bzr-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, _ := NewBzrRepo("", tempDir) if repo.CheckLocal() { t.Error("Bzr CheckLocal does not identify non-Bzr location") } // Test NewRepo when there's no local. This should simply provide a working // instance without error based on looking at the remote localtion. _, nrerr := NewRepo("https://launchpad.net/govcstestbzrrepo", tempDir+"/govcstestbzrrepo") if nrerr != nil { t.Error(nrerr) } } func TestBzrPing(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-bzr-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewBzrRepo("https://launchpad.net/govcstestbzrrepo", tempDir) if err != nil { t.Error(err) } ping := repo.Ping() if !ping { t.Error("Bzr unable to ping working repo") } repo, err = NewBzrRepo("https://launchpad.net/ihopethisneverexistsbecauseitshouldnt", tempDir) if err != nil { t.Error(err) } ping = repo.Ping() if ping { t.Error("Bzr got a ping response from when it should not have") } } func TestBzrInit(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-bzr-tests") repoDir := tempDir + "/repo" if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewBzrRepo(repoDir, repoDir) if err != nil { t.Error(err) } err = repo.Init() if err != nil { t.Error(err) } v, err := repo.Version() if err != nil { t.Error(err) } if v != "0" { t.Errorf("Bzr Init returns wrong version: %s", v) } } ================================================ FILE: vendor/github.com/Masterminds/vcs/errors.go ================================================ package vcs import ( "errors" "fmt" ) // The vcs package provides ways to work with errors that hide the underlying // implementation details but make them accessible if needed. For basic errors // that do not have underlying implementation specific details or the underlying // details are not necessary there are errors for comparison. // // For example: // // ci, err := repo.CommitInfo("123") // if err == vcs.ErrRevisionUnavailable { // // The commit id was not available in the VCS. // } // // There are other times where getting the details are more useful. For example, // if you're performing a repo.Get() and an error occurs. In general you'll want // to consistently know it failed. But, you may want to know the underlying // details (opt-in) to them. For those cases there is a different form of error // handling. // // For example: // // err := repo.Get() // if err != nil { // // A RemoteError was returned. This has access to the output of the // // vcs command, original error, and has a consistent cross vcs message. // } // // The errors returned here can be used in type switches to detect the underlying // error. For example: // // switch err.(type) { // case *vcs.RemoteError: // // This an error connecting to a remote system. // } // // For more information on using type switches to detect error types you can // read the Go wiki at https://github.com/golang/go/wiki/Errors var ( // ErrWrongVCS is returned when an action is tried on the wrong VCS. ErrWrongVCS = errors.New("Wrong VCS detected") // ErrCannotDetectVCS is returned when VCS cannot be detected from URI string. ErrCannotDetectVCS = errors.New("Cannot detect VCS") // ErrWrongRemote occurs when the passed in remote does not match the VCS // configured endpoint. ErrWrongRemote = errors.New("The Remote does not match the VCS endpoint") // ErrRevisionUnavailable happens when commit revision information is // unavailable. ErrRevisionUnavailable = errors.New("Revision unavailable") ) // RemoteError is returned when an operation fails against a remote repo type RemoteError struct { vcsError } // NewRemoteError constructs a RemoteError func NewRemoteError(msg string, err error, out string) error { e := &RemoteError{} e.s = msg e.e = err e.o = out return e } // LocalError is returned when a local operation has an error type LocalError struct { vcsError } // NewLocalError constructs a LocalError func NewLocalError(msg string, err error, out string) error { e := &LocalError{} e.s = msg e.e = err e.o = out return e } type vcsError struct { s string e error // The original error o string // The output from executing the command } // Error implements the Error interface func (e *vcsError) Error() string { if e.e == nil { return e.s } return fmt.Sprintf("%s: %v", e.s, e.e) } // Original retrieves the underlying implementation specific error. func (e *vcsError) Original() error { return e.e } // Out retrieves the output of the original command that was run. func (e *vcsError) Out() string { return e.o } ================================================ FILE: vendor/github.com/Masterminds/vcs/errors_test.go ================================================ package vcs import ( "errors" "testing" ) func TestNewRemoteError(t *testing.T) { base := errors.New("Foo error") out := "This is a test" msg := "remote error msg" e := NewRemoteError(msg, base, out) switch e.(type) { case *RemoteError: // This is the right error type default: t.Error("Wrong error type returned from NewRemoteError") } } func TestNewLocalError(t *testing.T) { base := errors.New("Foo error") out := "This is a test" msg := "local error msg" e := NewLocalError(msg, base, out) switch e.(type) { case *LocalError: // This is the right error type default: t.Error("Wrong error type returned from NewLocalError") } } ================================================ FILE: vendor/github.com/Masterminds/vcs/git.go ================================================ package vcs import ( "bytes" "encoding/xml" "io/ioutil" "os" "os/exec" "path/filepath" "runtime" "strings" "time" ) // NewGitRepo creates a new instance of GitRepo. The remote and local directories // need to be passed in. func NewGitRepo(remote, local string) (*GitRepo, error) { ins := depInstalled("git") if !ins { return nil, NewLocalError("git is not installed", nil, "") } ltype, err := DetectVcsFromFS(local) // Found a VCS other than Git. Need to report an error. if err == nil && ltype != Git { return nil, ErrWrongVCS } r := &GitRepo{} r.setRemote(remote) r.setLocalPath(local) r.RemoteLocation = "origin" r.Logger = Logger // Make sure the local Git repo is configured the same as the remote when // A remote value was passed in. if err == nil && r.CheckLocal() { c := exec.Command("git", "config", "--get", "remote.origin.url") c.Dir = local c.Env = envForDir(c.Dir) out, err := c.CombinedOutput() if err != nil { return nil, NewLocalError("Unable to retrieve local repo information", err, string(out)) } localRemote := strings.TrimSpace(string(out)) if remote != "" && localRemote != remote { return nil, ErrWrongRemote } // If no remote was passed in but one is configured for the locally // checked out Git repo use that one. if remote == "" && localRemote != "" { r.setRemote(localRemote) } } return r, nil } // GitRepo implements the Repo interface for the Git source control. type GitRepo struct { base RemoteLocation string } // Vcs retrieves the underlying VCS being implemented. func (s GitRepo) Vcs() Type { return Git } // Get is used to perform an initial clone of a repository. func (s *GitRepo) Get() error { out, err := s.run("git", "clone", "--recursive", s.Remote(), s.LocalPath()) // There are some windows cases where Git cannot create the parent directory, // if it does not already exist, to the location it's trying to create the // repo. Catch that error and try to handle it. if err != nil && s.isUnableToCreateDir(err) { basePath := filepath.Dir(filepath.FromSlash(s.LocalPath())) if _, err := os.Stat(basePath); os.IsNotExist(err) { err = os.MkdirAll(basePath, 0755) if err != nil { return NewLocalError("Unable to create directory", err, "") } out, err = s.run("git", "clone", s.Remote(), s.LocalPath()) if err != nil { return NewRemoteError("Unable to get repository", err, string(out)) } return err } } else if err != nil { return NewRemoteError("Unable to get repository", err, string(out)) } return nil } // Init initializes a git repository at local location. func (s *GitRepo) Init() error { out, err := s.run("git", "init", s.LocalPath()) // There are some windows cases where Git cannot create the parent directory, // if it does not already exist, to the location it's trying to create the // repo. Catch that error and try to handle it. if err != nil && s.isUnableToCreateDir(err) { basePath := filepath.Dir(filepath.FromSlash(s.LocalPath())) if _, err := os.Stat(basePath); os.IsNotExist(err) { err = os.MkdirAll(basePath, 0755) if err != nil { return NewLocalError("Unable to initialize repository", err, "") } out, err = s.run("git", "init", s.LocalPath()) if err != nil { return NewLocalError("Unable to initialize repository", err, string(out)) } return nil } } else if err != nil { return NewLocalError("Unable to initialize repository", err, string(out)) } return nil } // Update performs an Git fetch and pull to an existing checkout. func (s *GitRepo) Update() error { // Perform a fetch to make sure everything is up to date. out, err := s.RunFromDir("git", "fetch", "--tags", s.RemoteLocation) if err != nil { return NewRemoteError("Unable to update repository", err, string(out)) } // When in a detached head state, such as when an individual commit is checked // out do not attempt a pull. It will cause an error. detached, err := isDetachedHead(s.LocalPath()) if err != nil { return NewLocalError("Unable to update repository", err, "") } if detached { return nil } out, err = s.RunFromDir("git", "pull") if err != nil { return NewRemoteError("Unable to update repository", err, string(out)) } return s.defendAgainstSubmodules() } // UpdateVersion sets the version of a package currently checked out via Git. func (s *GitRepo) UpdateVersion(version string) error { out, err := s.RunFromDir("git", "checkout", version) if err != nil { return NewLocalError("Unable to update checked out version", err, string(out)) } return s.defendAgainstSubmodules() } // defendAgainstSubmodules tries to keep repo state sane in the event of // submodules. Or nested submodules. What a great idea, submodules. func (s *GitRepo) defendAgainstSubmodules() error { // First, update them to whatever they should be, if there should happen to be any. out, err := s.RunFromDir("git", "submodule", "update", "--init", "--recursive") if err != nil { return NewLocalError("Unexpected error while defensively updating submodules", err, string(out)) } // Now, do a special extra-aggressive clean in case changing versions caused // one or more submodules to go away. out, err = s.RunFromDir("git", "clean", "-x", "-d", "-f", "-f") if err != nil { return NewLocalError("Unexpected error while defensively cleaning up after possible derelict submodule directories", err, string(out)) } // Then, repeat just in case there are any nested submodules that went away. out, err = s.RunFromDir("git", "submodule", "foreach", "--recursive", "git clean -x -d -f -f") if err != nil { return NewLocalError("Unexpected error while defensively cleaning up after possible derelict nested submodule directories", err, string(out)) } return nil } // Version retrieves the current version. func (s *GitRepo) Version() (string, error) { out, err := s.RunFromDir("git", "rev-parse", "HEAD") if err != nil { return "", NewLocalError("Unable to retrieve checked out version", err, string(out)) } return strings.TrimSpace(string(out)), nil } // Current returns the current version-ish. This means: // * Branch name if on the tip of the branch // * Tag if on a tag // * Otherwise a revision id func (s *GitRepo) Current() (string, error) { out, err := s.RunFromDir("git", "symbolic-ref", "HEAD") if err == nil { o := bytes.TrimSpace(bytes.TrimPrefix(out, []byte("refs/heads/"))) return string(o), nil } v, err := s.Version() if err != nil { return "", err } ts, err := s.TagsFromCommit(v) if err != nil { return "", err } if len(ts) > 0 { return ts[0], nil } return v, nil } // Date retrieves the date on the latest commit. func (s *GitRepo) Date() (time.Time, error) { out, err := s.RunFromDir("git", "log", "-1", "--date=iso", "--pretty=format:%cd") if err != nil { return time.Time{}, NewLocalError("Unable to retrieve revision date", err, string(out)) } t, err := time.Parse(longForm, string(out)) if err != nil { return time.Time{}, NewLocalError("Unable to retrieve revision date", err, string(out)) } return t, nil } // Branches returns a list of available branches on the RemoteLocation func (s *GitRepo) Branches() ([]string, error) { out, err := s.RunFromDir("git", "show-ref") if err != nil { return []string{}, NewLocalError("Unable to retrieve branches", err, string(out)) } branches := s.referenceList(string(out), `(?m-s)(?:`+s.RemoteLocation+`)/(\S+)$`) return branches, nil } // Tags returns a list of available tags on the RemoteLocation func (s *GitRepo) Tags() ([]string, error) { out, err := s.RunFromDir("git", "show-ref") if err != nil { return []string{}, NewLocalError("Unable to retrieve tags", err, string(out)) } tags := s.referenceList(string(out), `(?m-s)(?:tags)/(\S+)$`) return tags, nil } // CheckLocal verifies the local location is a Git repo. func (s *GitRepo) CheckLocal() bool { if _, err := os.Stat(s.LocalPath() + "/.git"); err == nil { return true } return false } // IsReference returns if a string is a reference. A reference can be a // commit id, branch, or tag. func (s *GitRepo) IsReference(r string) bool { _, err := s.RunFromDir("git", "rev-parse", "--verify", r) if err == nil { return true } // Some refs will fail rev-parse. For example, a remote branch that has // not been checked out yet. This next step should pickup the other // possible references. _, err = s.RunFromDir("git", "show-ref", r) return err == nil } // IsDirty returns if the checkout has been modified from the checked // out reference. func (s *GitRepo) IsDirty() bool { out, err := s.RunFromDir("git", "diff") return err != nil || len(out) != 0 } // CommitInfo retrieves metadata about a commit. func (s *GitRepo) CommitInfo(id string) (*CommitInfo, error) { fm := `--pretty=format:"%H%an <%ae>%aD%s"` out, err := s.RunFromDir("git", "log", id, fm, "-1") if err != nil { return nil, ErrRevisionUnavailable } cis := struct { Commit string `xml:"commit"` Author string `xml:"author"` Date string `xml:"date"` Message string `xml:"message"` }{} err = xml.Unmarshal(out, &cis) if err != nil { return nil, NewLocalError("Unable to retrieve commit information", err, string(out)) } t, err := time.Parse("Mon, _2 Jan 2006 15:04:05 -0700", cis.Date) if err != nil { return nil, NewLocalError("Unable to retrieve commit information", err, string(out)) } ci := &CommitInfo{ Commit: cis.Commit, Author: cis.Author, Date: t, Message: cis.Message, } return ci, nil } // TagsFromCommit retrieves tags from a commit id. func (s *GitRepo) TagsFromCommit(id string) ([]string, error) { // This is imperfect and a better method would be great. var re []string out, err := s.RunFromDir("git", "show-ref", "-d") if err != nil { return []string{}, NewLocalError("Unable to retrieve tags", err, string(out)) } lines := strings.Split(string(out), "\n") var list []string for _, i := range lines { if strings.HasPrefix(strings.TrimSpace(i), id) { list = append(list, i) } } tags := s.referenceList(strings.Join(list, "\n"), `(?m-s)(?:tags)/(\S+)$`) for _, t := range tags { // Dereferenced tags have ^{} appended to them. re = append(re, strings.TrimSuffix(t, "^{}")) } return re, nil } // Ping returns if remote location is accessible. func (s *GitRepo) Ping() bool { c := exec.Command("git", "ls-remote", s.Remote()) // If prompted for a username and password, which GitHub does for all things // not public, it's considered not available. To make it available the // remote needs to be different. c.Env = mergeEnvLists([]string{"GIT_TERMINAL_PROMPT=0"}, os.Environ()) _, err := c.CombinedOutput() return err == nil } // EscapePathSeparator escapes the path separator by replacing it with several. // Note: this is harmless on Unix, and needed on Windows. func EscapePathSeparator(path string) string { switch runtime.GOOS { case `windows`: // On Windows, triple all path separators. // Needed to escape backslash(s) preceding doublequotes, // because of how Windows strings treats backslash+doublequote combo, // and Go seems to be implicitly passing around a doublequoted string on Windows, // so we cannot use default string instead. // See: https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ // e.g., C:\foo\bar\ -> C:\\\foo\\\bar\\\ // used with --prefix, like this: --prefix=C:\foo\bar\ -> --prefix=C:\\\foo\\\bar\\\ return strings.Replace(path, string(os.PathSeparator), string(os.PathSeparator)+string(os.PathSeparator)+string(os.PathSeparator), -1) default: return path } } // ExportDir exports the current revision to the passed in directory. func (s *GitRepo) ExportDir(dir string) error { var path string // Without the trailing / there can be problems. if !strings.HasSuffix(dir, string(os.PathSeparator)) { dir = dir + string(os.PathSeparator) } // checkout-index on some systems, such as some Windows cases, does not // create the parent directory to export into if it does not exist. Explicitly // creating it. err := os.MkdirAll(dir, 0755) if err != nil { return NewLocalError("Unable to create directory", err, "") } path = EscapePathSeparator(dir) out, err := s.RunFromDir("git", "checkout-index", "-f", "-a", "--prefix="+path) s.log(out) if err != nil { return NewLocalError("Unable to export source", err, string(out)) } // and now, the horror of submodules path = EscapePathSeparator(dir + "$path" + string(os.PathSeparator)) out, err = s.RunFromDir("git", "submodule", "foreach", "--recursive", "git checkout-index -f -a --prefix="+path) s.log(out) if err != nil { return NewLocalError("Error while exporting submodule sources", err, string(out)) } return nil } // isDetachedHead will detect if git repo is in "detached head" state. func isDetachedHead(dir string) (bool, error) { p := filepath.Join(dir, ".git", "HEAD") contents, err := ioutil.ReadFile(p) if err != nil { return false, err } contents = bytes.TrimSpace(contents) if bytes.HasPrefix(contents, []byte("ref: ")) { return false, nil } return true, nil } // isUnableToCreateDir checks for an error in Init() to see if an error // where the parent directory of the VCS local path doesn't exist. This is // done in a multi-lingual manner. func (s *GitRepo) isUnableToCreateDir(err error) bool { msg := err.Error() if strings.HasPrefix(msg, "could not create work tree dir") || strings.HasPrefix(msg, "不能创建工作区目录") || strings.HasPrefix(msg, "no s'ha pogut crear el directori d'arbre de treball") || strings.HasPrefix(msg, "impossible de créer le répertoire de la copie de travail") || strings.HasPrefix(msg, "kunde inte skapa arbetskatalogen") || (strings.HasPrefix(msg, "Konnte Arbeitsverzeichnis") && strings.Contains(msg, "nicht erstellen")) || (strings.HasPrefix(msg, "작업 디렉터리를") && strings.Contains(msg, "만들 수 없습니다")) { return true } return false } ================================================ FILE: vendor/github.com/Masterminds/vcs/git_test.go ================================================ package vcs import ( "fmt" "io/ioutil" "path/filepath" "time" //"log" "os" "testing" ) // Canary test to ensure GitRepo implements the Repo interface. var _ Repo = &GitRepo{} // To verify git is working we perform integration testing // with a known git service. func TestGit(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-git-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewGitRepo("https://github.com/Masterminds/VCSTestRepo", tempDir+"/VCSTestRepo") if err != nil { t.Error(err) } if repo.Vcs() != Git { t.Error("Git is detecting the wrong type") } // Check the basic getters. if repo.Remote() != "https://github.com/Masterminds/VCSTestRepo" { t.Error("Remote not set properly") } if repo.LocalPath() != tempDir+"/VCSTestRepo" { t.Error("Local disk location not set properly") } //Logger = log.New(os.Stdout, "", log.LstdFlags) // Do an initial clone. err = repo.Get() if err != nil { t.Errorf("Unable to clone Git repo. Err was %s", err) } // Verify Git repo is a Git repo if !repo.CheckLocal() { t.Error("Problem checking out repo or Git CheckLocal is not working") } // Test internal lookup mechanism used outside of Git specific functionality. ltype, err := DetectVcsFromFS(tempDir + "/VCSTestRepo") if err != nil { t.Error("detectVcsFromFS unable to Git repo") } if ltype != Git { t.Errorf("detectVcsFromFS detected %s instead of Git type", ltype) } // Test NewRepo on existing checkout. This should simply provide a working // instance without error based on looking at the local directory. nrepo, nrerr := NewRepo("https://github.com/Masterminds/VCSTestRepo", tempDir+"/VCSTestRepo") if nrerr != nil { t.Error(nrerr) } // Verify the right oject is returned. It will check the local repo type. if !nrepo.CheckLocal() { t.Error("Wrong version returned from NewRepo") } // Perform an update. err = repo.Update() if err != nil { t.Error(err) } v, err := repo.Current() if err != nil { t.Errorf("Error trying Git Current: %s", err) } if v != "master" { t.Errorf("Current failed to detect Git on tip of master. Got version: %s", v) } // Set the version using the short hash. err = repo.UpdateVersion("806b07b") if err != nil { t.Errorf("Unable to update Git repo version. Err was %s", err) } // Once a ref has been checked out the repo is in a detached head state. // Trying to pull in an update in this state will cause an error. Update // should cleanly handle this. Pulling on a branch (tested elsewhere) and // skipping that here. err = repo.Update() if err != nil { t.Error(err) } // Use Version to verify we are on the right version. v, err = repo.Version() if v != "806b07b08faa21cfbdae93027904f80174679402" { t.Error("Error checking checked out Git version") } if err != nil { t.Error(err) } v, err = repo.Current() if err != nil { t.Errorf("Error trying Git Current for ref: %s", err) } if v != "806b07b08faa21cfbdae93027904f80174679402" { t.Errorf("Current failed to detect Git on ref of branch. Got version: %s", v) } // Use Date to verify we are on the right commit. d, err := repo.Date() if d.Format(longForm) != "2015-07-29 09:46:39 -0400" { t.Error("Error checking checked out Git commit date") } if err != nil { t.Error(err) } // Verify that we can set the version something other than short hash err = repo.UpdateVersion("master") if err != nil { t.Errorf("Unable to update Git repo version. Err was %s", err) } err = repo.UpdateVersion("806b07b08faa21cfbdae93027904f80174679402") if err != nil { t.Errorf("Unable to update Git repo version. Err was %s", err) } v, err = repo.Version() if v != "806b07b08faa21cfbdae93027904f80174679402" { t.Error("Error checking checked out Git version") } if err != nil { t.Error(err) } tags, err := repo.Tags() if err != nil { t.Error(err) } var hasRelTag bool var hasOffMasterTag bool for _, tv := range tags { if tv == "1.0.0" { hasRelTag = true } else if tv == "off-master-tag" { hasOffMasterTag = true } } if !hasRelTag { t.Error("Git tags unable to find release tag on master") } if !hasOffMasterTag { t.Error("Git tags did not fetch tags not on master") } tags, err = repo.TagsFromCommit("74dd547545b7df4aa285bcec1b54e2b76f726395") if err != nil { t.Error(err) } if len(tags) != 0 { t.Error("Git is incorrectly returning tags for a commit") } tags, err = repo.TagsFromCommit("30605f6ac35fcb075ad0bfa9296f90a7d891523e") if err != nil { t.Error(err) } if len(tags) != 1 || tags[0] != "1.0.0" { t.Error("Git is incorrectly returning tags for a commit") } branches, err := repo.Branches() if err != nil { t.Error(err) } // The branches should be HEAD, master, other, and test. if branches[3] != "test" { t.Error("Git is incorrectly returning branches") } if !repo.IsReference("1.0.0") { t.Error("Git is reporting a reference is not one") } if repo.IsReference("foo") { t.Error("Git is reporting a non-existent reference is one") } if repo.IsDirty() { t.Error("Git incorrectly reporting dirty") } ci, err := repo.CommitInfo("806b07b08faa21cfbdae93027904f80174679402") if err != nil { t.Error(err) } if ci.Commit != "806b07b08faa21cfbdae93027904f80174679402" { t.Error("Git.CommitInfo wrong commit id") } if ci.Author != "Matt Farina " { t.Error("Git.CommitInfo wrong author") } if ci.Message != "Update README.md" { t.Error("Git.CommitInfo wrong message") } ti, err := time.Parse(time.RFC1123Z, "Wed, 29 Jul 2015 09:46:39 -0400") if err != nil { t.Error(err) } if !ti.Equal(ci.Date) { t.Error("Git.CommitInfo wrong date") } _, err = repo.CommitInfo("asdfasdfasdf") if err != ErrRevisionUnavailable { t.Error("Git didn't return expected ErrRevisionUnavailable") } tempDir2, err := ioutil.TempDir("", "go-vcs-git-tests-export") if err != nil { t.Fatalf("Error creating temp directory: %s", err) } defer func() { err = os.RemoveAll(tempDir2) if err != nil { t.Error(err) } }() exportDir := filepath.Join(tempDir2, "src") err = repo.ExportDir(exportDir) if err != nil { t.Errorf("Unable to export Git repo. Err was %s", err) } _, err = os.Stat(filepath.Join(exportDir, "README.md")) if err != nil { t.Errorf("Error checking exported file in Git: %s", err) } _, err = os.Stat(filepath.Join(exportDir, string(repo.Vcs()))) if err != nil { if found := os.IsNotExist(err); !found { t.Errorf("Error checking exported metadata in Git: %s", err) } } else { t.Error("Error checking Git metadata. It exists.") } } func TestGitCheckLocal(t *testing.T) { // Verify repo.CheckLocal fails for non-Git directories. // TestGit is already checking on a valid repo tempDir, err := ioutil.TempDir("", "go-vcs-git-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, _ := NewGitRepo("", tempDir) if repo.CheckLocal() { t.Error("Git CheckLocal does not identify non-Git location") } // Test NewRepo when there's no local. This should simply provide a working // instance without error based on looking at the remote localtion. _, nrerr := NewRepo("https://github.com/Masterminds/VCSTestRepo", tempDir+"/VCSTestRepo") if nrerr != nil { t.Error(nrerr) } } func TestGitPing(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-git-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewGitRepo("https://github.com/Masterminds/VCSTestRepo", tempDir) if err != nil { t.Error(err) } ping := repo.Ping() if !ping { t.Error("Git unable to ping working repo") } repo, err = NewGitRepo("https://github.com/Masterminds/ihopethisneverexistsbecauseitshouldnt", tempDir) if err != nil { t.Error(err) } ping = repo.Ping() if ping { t.Error("Git got a ping response from when it should not have") } } func TestGitInit(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-git-tests") repoDir := tempDir + "/repo" if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewGitRepo(repoDir, repoDir) if err != nil { t.Error(err) } err = repo.Init() if err != nil { t.Error(err) } _, err = repo.RunFromDir("git", "status") if err != nil { t.Error(err) } } func TestGitSubmoduleHandling(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-git-submodule-tests") if err != nil { t.Fatal(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() dumplocal := func(err error) string { if terr, ok := err.(*LocalError); ok { return fmt.Sprintf("msg: %s\norig: %s\nout: %s", terr.Error(), terr.Original(), terr.Out()) } return err.Error() } subdirExists := func(dir ...string) bool { _, err := os.Stat(filepath.Join(append([]string{tempDir}, dir...)...)) return err == nil } // Initial clone should get version with two submodules, each of which have // their own submodule repo, err := NewGitRepo("https://github.com/sdboyer/subm", tempDir) if err != nil { t.Fatal(dumplocal(err)) } err = repo.Get() if err != nil { t.Fatalf("unable to clone Git repo. Err was %s", dumplocal(err)) } // Verify we are on the right version. v, err := repo.Version() if v != "18e3a5f6fc7f6d577e732e7a5ab2caf990efbf8f" { t.Fatalf("did not start from expected rev, tests could fail - bailing out (got %s)", v) } if err != nil { t.Fatal(dumplocal(err)) } if !subdirExists("subm1", ".git") { t.Fatal("subm1 submodule does not exist on initial clone/checkout") } if !subdirExists("subm1", "dep-test", ".git") { t.Fatal("dep-test submodule nested under subm1 does not exist on initial clone/checkout") } if !subdirExists("subm-again", ".git") { t.Fatal("subm-again submodule does not exist on initial clone/checkout") } if !subdirExists("subm-again", "dep-test", ".git") { t.Fatal("dep-test submodule nested under subm-again does not exist on initial clone/checkout") } // Now switch to version with no submodules, make sure they all go away err = repo.UpdateVersion("e677f82015f72ac1c8fafa66b5463163b3597af2") if err != nil { t.Fatalf("checking out needed version failed with err: %s", dumplocal(err)) } if subdirExists("subm1") { t.Fatal("checking out version without submodule did not clean up immediate submodules") } if subdirExists("subm1", "dep-test") { t.Fatal("checking out version without submodule did not clean up nested submodules") } if subdirExists("subm-again") { t.Fatal("checking out version without submodule did not clean up immediate submodules") } if subdirExists("subm-again", "dep-test") { t.Fatal("checking out version without submodule did not clean up nested submodules") } err = repo.UpdateVersion("aaf7aa1bc4c3c682cc530eca8f80417088ee8540") if err != nil { t.Fatalf("checking out needed version failed with err: %s", dumplocal(err)) } if !subdirExists("subm1", ".git") { t.Fatal("checking out version with immediate submodule did not set up git subrepo") } err = repo.UpdateVersion("6cc4669af468f3b4f16e7e96275ad01ade5b522f") if err != nil { t.Fatalf("checking out needed version failed with err: %s", dumplocal(err)) } if !subdirExists("subm1", "dep-test", ".git") { t.Fatal("checking out version with nested submodule did not set up nested git subrepo") } err = repo.UpdateVersion("aaf7aa1bc4c3c682cc530eca8f80417088ee8540") if err != nil { t.Fatalf("checking out needed version failed with err: %s", dumplocal(err)) } if subdirExists("subm1", "dep-test") { t.Fatal("rolling back to version without nested submodule did not clean up the nested submodule") } err = repo.UpdateVersion("18e3a5f6fc7f6d577e732e7a5ab2caf990efbf8f") if err != nil { t.Fatalf("checking out needed version failed with err: %s", dumplocal(err)) } if !subdirExists("subm1", ".git") { t.Fatal("subm1 submodule does not exist after switch from other commit") } if !subdirExists("subm1", "dep-test", ".git") { t.Fatal("dep-test submodule nested under subm1 does not exist after switch from other commit") } if !subdirExists("subm-again", ".git") { t.Fatal("subm-again submodule does not exist after switch from other commit") } if !subdirExists("subm-again", "dep-test", ".git") { t.Fatal("dep-test submodule nested under subm-again does not exist after switch from other commit") } } func TestGitSubmoduleHandling2(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-git-submodule-tests2") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewGitRepo("https://github.com/cloudfoundry/sonde-go", tempDir+"/VCSTestRepo2") if err != nil { t.Error(err) } if repo.Vcs() != Git { t.Error("Git is detecting the wrong type") } // Check the basic getters. if repo.Remote() != "https://github.com/cloudfoundry/sonde-go" { t.Error("Remote not set properly") } if repo.LocalPath() != tempDir+"/VCSTestRepo2" { t.Error("Local disk location not set properly") } //Logger = log.New(os.Stdout, "", log.LstdFlags) // Do an initial clone. err = repo.Get() if err != nil { t.Errorf("Unable to clone Git repo. Err was %s", err) } // Verify Git repo is a Git repo if !repo.CheckLocal() { t.Error("Problem checking out repo or Git CheckLocal is not working") } // Test internal lookup mechanism used outside of Git specific functionality. ltype, err := DetectVcsFromFS(tempDir + "/VCSTestRepo2") if err != nil { t.Error("detectVcsFromFS unable to Git repo") } if ltype != Git { t.Errorf("detectVcsFromFS detected %s instead of Git type", ltype) } // Test NewRepo on existing checkout. This should simply provide a working // instance without error based on looking at the local directory. nrepo, nrerr := NewRepo("https://github.com/cloudfoundry/sonde-go", tempDir+"/VCSTestRepo2") if nrerr != nil { t.Error(nrerr) } // Verify the right oject is returned. It will check the local repo type. if !nrepo.CheckLocal() { t.Error("Wrong version returned from NewRepo") } // Perform an update. err = repo.Update() if err != nil { t.Error(err) } v, err := repo.Current() if err != nil { t.Errorf("Error trying Git Current: %s", err) } if v != "master" { t.Errorf("Current failed to detect Git on tip of master. Got version: %s", v) } tempDir2, err := ioutil.TempDir("", "go-vcs-git-tests-export") if err != nil { t.Fatalf("Error creating temp directory: %s", err) } defer func() { err = os.RemoveAll(tempDir2) if err != nil { t.Error(err) } }() exportDir := filepath.Join(tempDir2, "src") err = repo.ExportDir(exportDir) if err != nil { t.Errorf("Unable to export Git repo. Err was %s", err) } _, err = os.Stat(filepath.Join(exportDir, "README.md")) if err != nil { t.Errorf("Error checking exported file in Git: %s", err) } _, err = os.Stat(filepath.Join(filepath.Join(exportDir, "definitions"), "README.md")) if err != nil { t.Errorf("Error checking exported file in Git: %s", err) } _, err = os.Stat(filepath.Join(exportDir, string(repo.Vcs()))) if err != nil { if found := os.IsNotExist(err); !found { t.Errorf("Error checking exported metadata in Git: %s", err) } } else { t.Error("Error checking Git metadata. It exists.") } } ================================================ FILE: vendor/github.com/Masterminds/vcs/glide.yaml ================================================ package: github.com/Masterminds/vcs homepage: https://github.com/Masterminds/vcs license: MIT owners: - name: Matt Farina email: matt@mattfarina.com homepage: https://www.mattfarina.com/ import: [] ================================================ FILE: vendor/github.com/Masterminds/vcs/hg.go ================================================ package vcs import ( "bytes" "encoding/xml" "errors" "os" "os/exec" "regexp" "strings" "time" ) var hgDetectURL = regexp.MustCompile("default = (?P.+)\n") // NewHgRepo creates a new instance of HgRepo. The remote and local directories // need to be passed in. func NewHgRepo(remote, local string) (*HgRepo, error) { ins := depInstalled("hg") if !ins { return nil, NewLocalError("hg is not installed", nil, "") } ltype, err := DetectVcsFromFS(local) // Found a VCS other than Hg. Need to report an error. if err == nil && ltype != Hg { return nil, ErrWrongVCS } r := &HgRepo{} r.setRemote(remote) r.setLocalPath(local) r.Logger = Logger // Make sure the local Hg repo is configured the same as the remote when // A remote value was passed in. if err == nil && r.CheckLocal() { // An Hg repo was found so test that the URL there matches // the repo passed in here. c := exec.Command("hg", "paths") c.Dir = local c.Env = envForDir(c.Dir) out, err := c.CombinedOutput() if err != nil { return nil, NewLocalError("Unable to retrieve local repo information", err, string(out)) } m := hgDetectURL.FindStringSubmatch(string(out)) if m[1] != "" && m[1] != remote { return nil, ErrWrongRemote } // If no remote was passed in but one is configured for the locally // checked out Hg repo use that one. if remote == "" && m[1] != "" { r.setRemote(m[1]) } } return r, nil } // HgRepo implements the Repo interface for the Mercurial source control. type HgRepo struct { base } // Vcs retrieves the underlying VCS being implemented. func (s HgRepo) Vcs() Type { return Hg } // Get is used to perform an initial clone of a repository. func (s *HgRepo) Get() error { out, err := s.run("hg", "clone", s.Remote(), s.LocalPath()) if err != nil { return NewRemoteError("Unable to get repository", err, string(out)) } return nil } // Init will initialize a mercurial repository at local location. func (s *HgRepo) Init() error { out, err := s.run("hg", "init", s.LocalPath()) if err != nil { return NewLocalError("Unable to initialize repository", err, string(out)) } return nil } // Update performs a Mercurial pull to an existing checkout. func (s *HgRepo) Update() error { return s.UpdateVersion(``) } // UpdateVersion sets the version of a package currently checked out via Hg. func (s *HgRepo) UpdateVersion(version string) error { out, err := s.RunFromDir("hg", "pull") if err != nil { return NewLocalError("Unable to update checked out version", err, string(out)) } if len(strings.TrimSpace(version)) > 0 { out, err = s.RunFromDir("hg", "update", version) } else { out, err = s.RunFromDir("hg", "update") } if err != nil { return NewLocalError("Unable to update checked out version", err, string(out)) } return nil } // Version retrieves the current version. func (s *HgRepo) Version() (string, error) { c := s.CmdFromDir("hg", "--debug", "identify") stdout, stderr := new(bytes.Buffer), new(bytes.Buffer) c.Stdout = stdout c.Stderr = stderr if err := c.Run(); err != nil { return "", NewLocalError("Unable to retrieve checked out version", err, stderr.String()) } if stderr.Len() > 0 { // "hg --debug identify" can print out errors before it actually prints // the version. // https://github.com/Masterminds/vcs/issues/90 return "", NewLocalError("Unable to retrieve checked out version", errors.New("Error output printed before identify"), stderr.String()) } parts := strings.SplitN(stdout.String(), " ", 2) sha := parts[0] return strings.TrimSpace(sha), nil } // Current returns the current version-ish. This means: // * Branch name if on the tip of the branch // * Tag if on a tag // * Otherwise a revision id func (s *HgRepo) Current() (string, error) { out, err := s.RunFromDir("hg", "branch") if err != nil { return "", err } branch := strings.TrimSpace(string(out)) tip, err := s.CommitInfo("max(branch(" + branch + "))") if err != nil { return "", err } curr, err := s.Version() if err != nil { return "", err } if tip.Commit == curr { return branch, nil } ts, err := s.TagsFromCommit(curr) if err != nil { return "", err } if len(ts) > 0 { return ts[0], nil } return curr, nil } // Date retrieves the date on the latest commit. func (s *HgRepo) Date() (time.Time, error) { version, err := s.Version() if err != nil { return time.Time{}, NewLocalError("Unable to retrieve revision date", err, "") } out, err := s.RunFromDir("hg", "log", "-r", version, "--template", "{date|isodatesec}") if err != nil { return time.Time{}, NewLocalError("Unable to retrieve revision date", err, string(out)) } t, err := time.Parse(longForm, string(out)) if err != nil { return time.Time{}, NewLocalError("Unable to retrieve revision date", err, string(out)) } return t, nil } // CheckLocal verifies the local location is a Git repo. func (s *HgRepo) CheckLocal() bool { if _, err := os.Stat(s.LocalPath() + "/.hg"); err == nil { return true } return false } // Branches returns a list of available branches func (s *HgRepo) Branches() ([]string, error) { out, err := s.RunFromDir("hg", "branches") if err != nil { return []string{}, NewLocalError("Unable to retrieve branches", err, string(out)) } branches := s.referenceList(string(out), `(?m-s)^(\S+)`) return branches, nil } // Tags returns a list of available tags func (s *HgRepo) Tags() ([]string, error) { out, err := s.RunFromDir("hg", "tags") if err != nil { return []string{}, NewLocalError("Unable to retrieve tags", err, string(out)) } tags := s.referenceList(string(out), `(?m-s)^(\S+)`) return tags, nil } // IsReference returns if a string is a reference. A reference can be a // commit id, branch, or tag. func (s *HgRepo) IsReference(r string) bool { _, err := s.RunFromDir("hg", "log", "-r", r) return err == nil } // IsDirty returns if the checkout has been modified from the checked // out reference. func (s *HgRepo) IsDirty() bool { out, err := s.RunFromDir("hg", "diff") return err != nil || len(out) != 0 } // CommitInfo retrieves metadata about a commit. func (s *HgRepo) CommitInfo(id string) (*CommitInfo, error) { out, err := s.RunFromDir("hg", "log", "-r", id, "--style=xml") if err != nil { return nil, ErrRevisionUnavailable } type Author struct { Name string `xml:",chardata"` Email string `xml:"email,attr"` } type Logentry struct { Node string `xml:"node,attr"` Author Author `xml:"author"` Date string `xml:"date"` Msg string `xml:"msg"` } type Log struct { XMLName xml.Name `xml:"log"` Logs []Logentry `xml:"logentry"` } logs := &Log{} err = xml.Unmarshal(out, &logs) if err != nil { return nil, NewLocalError("Unable to retrieve commit information", err, string(out)) } if len(logs.Logs) == 0 { return nil, ErrRevisionUnavailable } ci := &CommitInfo{ Commit: logs.Logs[0].Node, Author: logs.Logs[0].Author.Name + " <" + logs.Logs[0].Author.Email + ">", Message: logs.Logs[0].Msg, } if logs.Logs[0].Date != "" { ci.Date, err = time.Parse(time.RFC3339, logs.Logs[0].Date) if err != nil { return nil, NewLocalError("Unable to retrieve commit information", err, string(out)) } } return ci, nil } // TagsFromCommit retrieves tags from a commit id. func (s *HgRepo) TagsFromCommit(id string) ([]string, error) { // Hg has a single tag per commit. If a second tag is added to a commit a // new commit is created and the tag is attached to that new commit. out, err := s.RunFromDir("hg", "log", "-r", id, "--style=xml") if err != nil { return []string{}, NewLocalError("Unable to retrieve tags", err, string(out)) } type Logentry struct { Node string `xml:"node,attr"` Tag string `xml:"tag"` } type Log struct { XMLName xml.Name `xml:"log"` Logs []Logentry `xml:"logentry"` } logs := &Log{} err = xml.Unmarshal(out, &logs) if err != nil { return []string{}, NewLocalError("Unable to retrieve tags", err, string(out)) } if len(logs.Logs) == 0 { return []string{}, NewLocalError("Unable to retrieve tags", err, string(out)) } t := strings.TrimSpace(logs.Logs[0].Tag) if t != "" { return []string{t}, nil } return []string{}, nil } // Ping returns if remote location is accessible. func (s *HgRepo) Ping() bool { _, err := s.run("hg", "identify", s.Remote()) return err == nil } // ExportDir exports the current revision to the passed in directory. func (s *HgRepo) ExportDir(dir string) error { out, err := s.RunFromDir("hg", "archive", dir) s.log(out) if err != nil { return NewLocalError("Unable to export source", err, string(out)) } return nil } ================================================ FILE: vendor/github.com/Masterminds/vcs/hg_test.go ================================================ package vcs import ( "io/ioutil" "os" "path/filepath" "strings" "testing" "time" ) // Canary test to ensure HgRepo implements the Repo interface. var _ Repo = &HgRepo{} // To verify hg is working we perform integration testing // with a known hg service. func TestHg(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-hg-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewHgRepo("https://bitbucket.org/mattfarina/testhgrepo", tempDir+"/testhgrepo") if err != nil { t.Error(err) } if repo.Vcs() != Hg { t.Error("Hg is detecting the wrong type") } // Check the basic getters. if repo.Remote() != "https://bitbucket.org/mattfarina/testhgrepo" { t.Error("Remote not set properly") } if repo.LocalPath() != tempDir+"/testhgrepo" { t.Error("Local disk location not set properly") } //Logger = log.New(os.Stdout, "", log.LstdFlags) // Do an initial clone. err = repo.Get() if err != nil { t.Errorf("Unable to clone Hg repo. Err was %s", err) } // Verify Hg repo is a Hg repo if !repo.CheckLocal() { t.Error("Problem checking out repo or Hg CheckLocal is not working") } // Test internal lookup mechanism used outside of Hg specific functionality. ltype, err := DetectVcsFromFS(tempDir + "/testhgrepo") if err != nil { t.Error("detectVcsFromFS unable to Hg repo") } if ltype != Hg { t.Errorf("detectVcsFromFS detected %s instead of Hg type", ltype) } // Test NewRepo on existing checkout. This should simply provide a working // instance without error based on looking at the local directory. nrepo, nrerr := NewRepo("https://bitbucket.org/mattfarina/testhgrepo", tempDir+"/testhgrepo") if nrerr != nil { t.Error(nrerr) } // Verify the right oject is returned. It will check the local repo type. if !nrepo.CheckLocal() { t.Error("Wrong version returned from NewRepo") } v, err := repo.Current() if err != nil { t.Errorf("Error trying Hg Current: %s", err) } if v != "default" { t.Errorf("Current failed to detect Hg on tip of default. Got version: %s", v) } // Set the version using the short hash. err = repo.UpdateVersion("a5494ba2177f") if err != nil { t.Errorf("Unable to update Hg repo version. Err was %s", err) } // Use Version to verify we are on the right version. v, err = repo.Version() if v != "a5494ba2177ff9ef26feb3c155dfecc350b1a8ef" { t.Errorf("Error checking checked out Hg version: %s", v) } if err != nil { t.Error(err) } v, err = repo.Current() if err != nil { t.Errorf("Error trying Hg Current for ref: %s", err) } if v != "a5494ba2177ff9ef26feb3c155dfecc350b1a8ef" { t.Errorf("Current failed to detect Hg on ref of branch. Got version: %s", v) } // Use Date to verify we are on the right commit. d, err := repo.Date() if err != nil { t.Error(err) } if d.Format(longForm) != "2015-07-30 16:14:08 -0400" { t.Error("Error checking checked out Hg commit date. Got wrong date:", d) } // Perform an update. err = repo.Update() if err != nil { t.Error(err) } v, err = repo.Version() if v != "9c6ccbca73e8a1351c834f33f57f1f7a0329ad35" { t.Errorf("Error checking checked out Hg version: %s", v) } if err != nil { t.Error(err) } tags, err := repo.Tags() if err != nil { t.Error(err) } if tags[1] != "1.0.0" { t.Error("Hg tags is not reporting the correct version") } tags, err = repo.TagsFromCommit("a5494ba2177f") if err != nil { t.Error(err) } if len(tags) != 0 { t.Error("Hg is incorrectly returning tags for a commit") } tags, err = repo.TagsFromCommit("d680e82228d2") if err != nil { t.Error(err) } if len(tags) != 1 || tags[0] != "1.0.0" { t.Error("Hg is incorrectly returning tags for a commit") } branches, err := repo.Branches() if err != nil { t.Error(err) } // The branches should be HEAD, master, and test. if branches[0] != "test" { t.Error("Hg is incorrectly returning branches") } if !repo.IsReference("1.0.0") { t.Error("Hg is reporting a reference is not one") } if !repo.IsReference("test") { t.Error("Hg is reporting a reference is not one") } if repo.IsReference("foo") { t.Error("Hg is reporting a non-existent reference is one") } if repo.IsDirty() { t.Error("Hg incorrectly reporting dirty") } ci, err := repo.CommitInfo("a5494ba2177f") if err != nil { t.Error(err) } if ci.Commit != "a5494ba2177ff9ef26feb3c155dfecc350b1a8ef" { t.Error("Hg.CommitInfo wrong commit id") } if ci.Author != "Matt Farina " { t.Error("Hg.CommitInfo wrong author") } if ci.Message != "A commit" { t.Error("Hg.CommitInfo wrong message") } ti := time.Unix(1438287248, 0) if !ti.Equal(ci.Date) { t.Error("Hg.CommitInfo wrong date") } _, err = repo.CommitInfo("asdfasdfasdf") if err != ErrRevisionUnavailable { t.Error("Hg didn't return expected ErrRevisionUnavailable") } tempDir2, err := ioutil.TempDir("", "go-vcs-hg-tests-export") if err != nil { t.Fatalf("Error creating temp directory: %s", err) } defer func() { err = os.RemoveAll(tempDir2) if err != nil { t.Error(err) } }() exportDir := filepath.Join(tempDir2, "src") err = repo.ExportDir(exportDir) if err != nil { t.Errorf("Unable to export Hg repo. Err was %s", err) } _, err = os.Stat(filepath.Join(exportDir, "Readme.md")) if err != nil { t.Errorf("Error checking exported file in Hg: %s", err) } _, err = os.Stat(filepath.Join(exportDir, string(repo.Vcs()))) if err != nil { if found := os.IsNotExist(err); !found { t.Errorf("Error checking exported metadata in Hg: %s", err) } } else { t.Error("Error checking Hg metadata. It exists.") } } func TestHgCheckLocal(t *testing.T) { // Verify repo.CheckLocal fails for non-Hg directories. // TestHg is already checking on a valid repo tempDir, err := ioutil.TempDir("", "go-vcs-hg-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, _ := NewHgRepo("", tempDir) if repo.CheckLocal() { t.Error("Hg CheckLocal does not identify non-Hg location") } // Test NewRepo when there's no local. This should simply provide a working // instance without error based on looking at the remote localtion. _, nrerr := NewRepo("https://bitbucket.org/mattfarina/testhgrepo", tempDir+"/testhgrepo") if nrerr != nil { t.Error(nrerr) } } func TestHgPing(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-hg-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewHgRepo("https://bitbucket.org/mattfarina/testhgrepo", tempDir) if err != nil { t.Error(err) } ping := repo.Ping() if !ping { t.Error("Hg unable to ping working repo") } repo, err = NewHgRepo("https://bitbucket.org/mattfarina/ihopethisneverexistsbecauseitshouldnt", tempDir) if err != nil { t.Error(err) } ping = repo.Ping() if ping { t.Error("Hg got a ping response from when it should not have") } } func TestHgInit(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-hg-tests") repoDir := tempDir + "/repo" if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewHgRepo(repoDir, repoDir) if err != nil { t.Error(err) } err = repo.Init() if err != nil { t.Error(err) } v, err := repo.Version() if err != nil { t.Error(err) } if !strings.HasPrefix(v, "000000") { t.Errorf("Hg Init reporting wrong initial version: %s", v) } } ================================================ FILE: vendor/github.com/Masterminds/vcs/repo.go ================================================ // Package vcs provides the ability to work with varying version control systems // (VCS), also known as source control systems (SCM) though the same interface. // // This package includes a function that attempts to detect the repo type from // the remote URL and return the proper type. For example, // // remote := "https://github.com/Masterminds/vcs" // local, _ := ioutil.TempDir("", "go-vcs") // repo, err := NewRepo(remote, local) // // In this case repo will be a GitRepo instance. NewRepo can detect the VCS for // numerous popular VCS and from the URL. For example, a URL ending in .git // that's not from one of the popular VCS will be detected as a Git repo and // the correct type will be returned. // // If you know the repository type and would like to create an instance of a // specific type you can use one of constructors for a type. They are NewGitRepo, // NewSvnRepo, NewBzrRepo, and NewHgRepo. The definition and usage is the same // as NewRepo. // // Once you have an object implementing the Repo interface the operations are // the same no matter which VCS you're using. There are some caveats. For // example, each VCS has its own version formats that need to be respected and // checkout out branches, if a branch is being worked with, is different in // each VCS. package vcs import ( "fmt" "io/ioutil" "log" "os" "os/exec" "regexp" "strings" "time" ) // Logger is where you can provide a logger, implementing the log.Logger interface, // where verbose output from each VCS will be written. The default logger does // not log data. To log data supply your own logger or change the output location // of the provided logger. var Logger *log.Logger func init() { // Initialize the logger to one that does not actually log anywhere. This is // to be overridden by the package user by setting vcs.Logger to a different // logger. Logger = log.New(ioutil.Discard, "go-vcs", log.LstdFlags) } const longForm = "2006-01-02 15:04:05 -0700" // Type describes the type of VCS type Type string // VCS types const ( NoVCS Type = "" Git Type = "git" Svn Type = "svn" Bzr Type = "bzr" Hg Type = "hg" ) // Repo provides an interface to work with repositories using different source // control systems such as Git, Bzr, Mercurial, and SVN. For implementations // of this interface see BzrRepo, GitRepo, HgRepo, and SvnRepo. type Repo interface { // Vcs retrieves the underlying VCS being implemented. Vcs() Type // Remote retrieves the remote location for a repo. Remote() string // LocalPath retrieves the local file system location for a repo. LocalPath() string // Get is used to perform an initial clone/checkout of a repository. Get() error // Initializes a new repository locally. Init() error // Update performs an update to an existing checkout of a repository. Update() error // UpdateVersion sets the version of a package of a repository. UpdateVersion(string) error // Version retrieves the current version. Version() (string, error) // Current retrieves the current version-ish. This is different from the // Version method. The output could be a branch name if on the tip of a // branch (git), a tag if on a tag, a revision if on a specific revision // that's not the tip of the branch. The values here vary based on the VCS. Current() (string, error) // Date retrieves the date on the latest commit. Date() (time.Time, error) // CheckLocal verifies the local location is of the correct VCS type CheckLocal() bool // Branches returns a list of available branches on the repository. Branches() ([]string, error) // Tags returns a list of available tags on the repository. Tags() ([]string, error) // IsReference returns if a string is a reference. A reference can be a // commit id, branch, or tag. IsReference(string) bool // IsDirty returns if the checkout has been modified from the checked // out reference. IsDirty() bool // CommitInfo retrieves metadata about a commit. CommitInfo(string) (*CommitInfo, error) // TagsFromCommit retrieves tags from a commit id. TagsFromCommit(string) ([]string, error) // Ping returns if remote location is accessible. Ping() bool // RunFromDir executes a command from repo's directory. RunFromDir(cmd string, args ...string) ([]byte, error) // CmdFromDir creates a new command that will be executed from repo's // directory. CmdFromDir(cmd string, args ...string) *exec.Cmd // ExportDir exports the current revision to the passed in directory. ExportDir(string) error } // NewRepo returns a Repo based on trying to detect the source control from the // remote and local locations. The appropriate implementation will be returned // or an ErrCannotDetectVCS if the VCS type cannot be detected. // Note, this function may make calls to the Internet to determind help determine // the VCS. func NewRepo(remote, local string) (Repo, error) { vtype, remote, err := detectVcsFromRemote(remote) // From the remote URL the VCS could not be detected. See if the local // repo contains enough information to figure out the VCS. The reason the // local repo is not checked first is because of the potential for VCS type // switches which will be detected in each of the type builders. if err == ErrCannotDetectVCS { vtype, err = DetectVcsFromFS(local) } if err != nil { return nil, err } switch vtype { case Git: return NewGitRepo(remote, local) case Svn: return NewSvnRepo(remote, local) case Hg: return NewHgRepo(remote, local) case Bzr: return NewBzrRepo(remote, local) } // Should never fall through to here but just in case. return nil, ErrCannotDetectVCS } // CommitInfo contains metadata about a commit. type CommitInfo struct { // The commit id Commit string // Who authored the commit Author string // Date of the commit Date time.Time // Commit message Message string } type base struct { remote, local string Logger *log.Logger } func (b *base) log(v interface{}) { b.Logger.Printf("%s", v) } // Remote retrieves the remote location for a repo. func (b *base) Remote() string { return b.remote } // LocalPath retrieves the local file system location for a repo. func (b *base) LocalPath() string { return b.local } func (b *base) setRemote(remote string) { b.remote = remote } func (b *base) setLocalPath(local string) { b.local = local } func (b base) run(cmd string, args ...string) ([]byte, error) { out, err := exec.Command(cmd, args...).CombinedOutput() b.log(out) if err != nil { err = fmt.Errorf("%s: %s", out, err) } return out, err } func (b *base) CmdFromDir(cmd string, args ...string) *exec.Cmd { c := exec.Command(cmd, args...) c.Dir = b.local c.Env = envForDir(c.Dir) return c } func (b *base) RunFromDir(cmd string, args ...string) ([]byte, error) { c := b.CmdFromDir(cmd, args...) out, err := c.CombinedOutput() return out, err } func (b *base) referenceList(c, r string) []string { var out []string re := regexp.MustCompile(r) for _, m := range re.FindAllStringSubmatch(c, -1) { out = append(out, m[1]) } return out } func envForDir(dir string) []string { env := os.Environ() return mergeEnvLists([]string{"PWD=" + dir}, env) } func mergeEnvLists(in, out []string) []string { NextVar: for _, inkv := range in { k := strings.SplitAfterN(inkv, "=", 2)[0] for i, outkv := range out { if strings.HasPrefix(outkv, k) { out[i] = inkv continue NextVar } } out = append(out, inkv) } return out } func depInstalled(name string) bool { if _, err := exec.LookPath(name); err != nil { return false } return true } ================================================ FILE: vendor/github.com/Masterminds/vcs/repo_test.go ================================================ package vcs import ( "fmt" "io/ioutil" "os" "testing" ) func ExampleNewRepo() { remote := "https://github.com/Masterminds/vcs" local, _ := ioutil.TempDir("", "go-vcs") repo, _ := NewRepo(remote, local) // Returns: instance of GitRepo repo.Vcs() // Returns Git as this is a Git repo err := repo.Get() // Pulls down a repo, or a checkout in the case of SVN, and returns an // error if that didn't happen successfully. if err != nil { fmt.Println(err) } err = repo.UpdateVersion("master") // Checkouts out a specific version. In most cases this can be a commit id, // branch, or tag. if err != nil { fmt.Println(err) } } func TestTypeSwitch(t *testing.T) { // To test repo type switching we checkout as SVN and then try to get it as // a git repo afterwards. tempDir, err := ioutil.TempDir("", "go-vcs-svn-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewSvnRepo("https://github.com/Masterminds/VCSTestRepo/trunk", tempDir+string(os.PathSeparator)+"VCSTestRepo") if err != nil { t.Error(err) } err = repo.Get() if err != nil { t.Errorf("Unable to checkout SVN repo for repo switching tests. Err was %s", err) } _, err = NewRepo("https://github.com/Masterminds/VCSTestRepo", tempDir+string(os.PathSeparator)+"VCSTestRepo") if err != ErrWrongVCS { t.Errorf("Not detecting repo switch from SVN to Git") } } func TestDepInstalled(t *testing.T) { i := depInstalled("git") if !i { t.Error("depInstalled not finding installed dep.") } i = depInstalled("thisreallyisntinstalled") if i { t.Error("depInstalled finding not installed dep.") } } ================================================ FILE: vendor/github.com/Masterminds/vcs/svn.go ================================================ package vcs import ( "encoding/xml" "fmt" "os" "os/exec" "path/filepath" "runtime" "strings" "time" ) // NewSvnRepo creates a new instance of SvnRepo. The remote and local directories // need to be passed in. The remote location should include the branch for SVN. // For example, if the package is https://github.com/Masterminds/cookoo/ the remote // should be https://github.com/Masterminds/cookoo/trunk for the trunk branch. func NewSvnRepo(remote, local string) (*SvnRepo, error) { ins := depInstalled("svn") if !ins { return nil, NewLocalError("svn is not installed", nil, "") } ltype, err := DetectVcsFromFS(local) // Found a VCS other than Svn. Need to report an error. if err == nil && ltype != Svn { return nil, ErrWrongVCS } r := &SvnRepo{} r.setRemote(remote) r.setLocalPath(local) r.Logger = Logger // Make sure the local SVN repo is configured the same as the remote when // A remote value was passed in. if err == nil && r.CheckLocal() { // An SVN repo was found so test that the URL there matches // the repo passed in here. out, err := exec.Command("svn", "info", local).CombinedOutput() if err != nil { return nil, NewLocalError("Unable to retrieve local repo information", err, string(out)) } detectedRemote, err := detectRemoteFromInfoCommand(string(out)) if err != nil { return nil, NewLocalError("Unable to retrieve local repo information", err, string(out)) } if detectedRemote != "" && remote != "" && detectedRemote != remote { return nil, ErrWrongRemote } // If no remote was passed in but one is configured for the locally // checked out Svn repo use that one. if remote == "" && detectedRemote != "" { r.setRemote(detectedRemote) } } return r, nil } // SvnRepo implements the Repo interface for the Svn source control. type SvnRepo struct { base } // Vcs retrieves the underlying VCS being implemented. func (s SvnRepo) Vcs() Type { return Svn } // Get is used to perform an initial checkout of a repository. // Note, because SVN isn't distributed this is a checkout without // a clone. func (s *SvnRepo) Get() error { remote := s.Remote() if strings.HasPrefix(remote, "/") { remote = "file://" + remote } else if runtime.GOOS == "windows" && filepath.VolumeName(remote) != "" { remote = "file:///" + remote } out, err := s.run("svn", "checkout", remote, s.LocalPath()) if err != nil { return NewRemoteError("Unable to get repository", err, string(out)) } return nil } // Init will create a svn repository at remote location. func (s *SvnRepo) Init() error { out, err := s.run("svnadmin", "create", s.Remote()) if err != nil && s.isUnableToCreateDir(err) { basePath := filepath.Dir(filepath.FromSlash(s.Remote())) if _, err := os.Stat(basePath); os.IsNotExist(err) { err = os.MkdirAll(basePath, 0755) if err != nil { return NewLocalError("Unable to initialize repository", err, "") } out, err = s.run("svnadmin", "create", s.Remote()) if err != nil { return NewLocalError("Unable to initialize repository", err, string(out)) } return nil } } else if err != nil { return NewLocalError("Unable to initialize repository", err, string(out)) } return nil } // Update performs an SVN update to an existing checkout. func (s *SvnRepo) Update() error { out, err := s.RunFromDir("svn", "update") if err != nil { return NewRemoteError("Unable to update repository", err, string(out)) } return err } // UpdateVersion sets the version of a package currently checked out via SVN. func (s *SvnRepo) UpdateVersion(version string) error { out, err := s.RunFromDir("svn", "update", "-r", version) if err != nil { return NewRemoteError("Unable to update checked out version", err, string(out)) } return nil } // Version retrieves the current version. func (s *SvnRepo) Version() (string, error) { type Commit struct { Revision string `xml:"revision,attr"` } type Info struct { Commit Commit `xml:"entry>commit"` } out, err := s.RunFromDir("svn", "info", "--xml") if err != nil { return "", NewLocalError("Unable to retrieve checked out version", err, string(out)) } s.log(out) infos := &Info{} err = xml.Unmarshal(out, &infos) if err != nil { return "", NewLocalError("Unable to retrieve checked out version", err, string(out)) } return infos.Commit.Revision, nil } // Current returns the current version-ish. This means: // * HEAD if on the tip. // * Otherwise a revision id func (s *SvnRepo) Current() (string, error) { tip, err := s.CommitInfo("HEAD") if err != nil { return "", err } curr, err := s.Version() if err != nil { return "", err } if tip.Commit == curr { return "HEAD", nil } return curr, nil } // Date retrieves the date on the latest commit. func (s *SvnRepo) Date() (time.Time, error) { version, err := s.Version() if err != nil { return time.Time{}, NewLocalError("Unable to retrieve revision date", err, "") } out, err := s.RunFromDir("svn", "pget", "svn:date", "--revprop", "-r", version) if err != nil { return time.Time{}, NewLocalError("Unable to retrieve revision date", err, string(out)) } const longForm = "2006-01-02T15:04:05.000000Z" t, err := time.Parse(longForm, strings.TrimSpace(string(out))) if err != nil { return time.Time{}, NewLocalError("Unable to retrieve revision date", err, string(out)) } return t, nil } // CheckLocal verifies the local location is an SVN repo. func (s *SvnRepo) CheckLocal() bool { pth, err := filepath.Abs(s.LocalPath()) if err != nil { s.log(err.Error()) return false } if _, err := os.Stat(filepath.Join(pth, ".svn")); err == nil { return true } oldpth := pth for oldpth != pth { pth = filepath.Dir(pth) if _, err := os.Stat(filepath.Join(pth, ".svn")); err == nil { return true } } return false } // Tags returns []string{} as there are no formal tags in SVN. Tags are a // convention in SVN. They are typically implemented as a copy of the trunk and // placed in the /tags/[tag name] directory. Since this is a convention the // expectation is to checkout a tag the correct subdirectory will be used // as the path. For more information see: // http://svnbook.red-bean.com/en/1.7/svn.branchmerge.tags.html func (s *SvnRepo) Tags() ([]string, error) { return []string{}, nil } // Branches returns []string{} as there are no formal branches in SVN. Branches // are a convention. They are typically implemented as a copy of the trunk and // placed in the /branches/[tag name] directory. Since this is a convention the // expectation is to checkout a branch the correct subdirectory will be used // as the path. For more information see: // http://svnbook.red-bean.com/en/1.7/svn.branchmerge.using.html func (s *SvnRepo) Branches() ([]string, error) { return []string{}, nil } // IsReference returns if a string is a reference. A reference is a commit id. // Branches and tags are part of the path. func (s *SvnRepo) IsReference(r string) bool { out, err := s.RunFromDir("svn", "log", "-r", r) // This is a complete hack. There must be a better way to do this. Pull // requests welcome. When the reference isn't real you get a line of // repeated - followed by an empty line. If the reference is real there // is commit information in addition to those. So, we look for responses // over 2 lines long. lines := strings.Split(string(out), "\n") if err == nil && len(lines) > 2 { return true } return false } // IsDirty returns if the checkout has been modified from the checked // out reference. func (s *SvnRepo) IsDirty() bool { out, err := s.RunFromDir("svn", "diff") return err != nil || len(out) != 0 } // CommitInfo retrieves metadata about a commit. func (s *SvnRepo) CommitInfo(id string) (*CommitInfo, error) { // There are cases where Svn log doesn't return anything for HEAD or BASE. // svn info does provide details for these but does not have elements like // the commit message. if id == "HEAD" || id == "BASE" { type Commit struct { Revision string `xml:"revision,attr"` } type Info struct { Commit Commit `xml:"entry>commit"` } out, err := s.RunFromDir("svn", "info", "-r", id, "--xml") if err != nil { return nil, NewLocalError("Unable to retrieve commit information", err, string(out)) } infos := &Info{} err = xml.Unmarshal(out, &infos) if err != nil { return nil, NewLocalError("Unable to retrieve commit information", err, string(out)) } id = infos.Commit.Revision if id == "" { return nil, ErrRevisionUnavailable } } out, err := s.RunFromDir("svn", "log", "-r", id, "--xml") if err != nil { return nil, NewRemoteError("Unable to retrieve commit information", err, string(out)) } type Logentry struct { Author string `xml:"author"` Date string `xml:"date"` Msg string `xml:"msg"` } type Log struct { XMLName xml.Name `xml:"log"` Logs []Logentry `xml:"logentry"` } logs := &Log{} err = xml.Unmarshal(out, &logs) if err != nil { return nil, NewLocalError("Unable to retrieve commit information", err, string(out)) } if len(logs.Logs) == 0 { return nil, ErrRevisionUnavailable } ci := &CommitInfo{ Commit: id, Author: logs.Logs[0].Author, Message: logs.Logs[0].Msg, } if len(logs.Logs[0].Date) > 0 { ci.Date, err = time.Parse(time.RFC3339Nano, logs.Logs[0].Date) if err != nil { return nil, NewLocalError("Unable to retrieve commit information", err, string(out)) } } return ci, nil } // TagsFromCommit retrieves tags from a commit id. func (s *SvnRepo) TagsFromCommit(id string) ([]string, error) { // Svn tags are a convention implemented as paths. See the details on the // Tag() method for more information. return []string{}, nil } // Ping returns if remote location is accessible. func (s *SvnRepo) Ping() bool { _, err := s.run("svn", "--non-interactive", "info", s.Remote()) return err == nil } // ExportDir exports the current revision to the passed in directory. func (s *SvnRepo) ExportDir(dir string) error { out, err := s.RunFromDir("svn", "export", ".", dir) s.log(out) if err != nil { return NewLocalError("Unable to export source", err, string(out)) } return nil } // isUnableToCreateDir checks for an error in Init() to see if an error // where the parent directory of the VCS local path doesn't exist. func (s *SvnRepo) isUnableToCreateDir(err error) bool { msg := err.Error() return strings.HasPrefix(msg, "E000002") } // detectRemoteFromInfoCommand finds the remote url from the `svn info` // command's output without using a regex. We avoid regex because URLs // are notoriously complex to accurately match with a regex and // splitting strings is less complex and often faster func detectRemoteFromInfoCommand(infoOut string) (string, error) { sBytes := []byte(infoOut) urlIndex := strings.Index(infoOut, "URL: ") if urlIndex == -1 { return "", fmt.Errorf("Remote not specified in svn info") } urlEndIndex := strings.Index(string(sBytes[urlIndex:]), "\n") if urlEndIndex == -1 { urlEndIndex = strings.Index(string(sBytes[urlIndex:]), "\r") if urlEndIndex == -1 { return "", fmt.Errorf("Unable to parse remote URL for svn info") } } return string(sBytes[(urlIndex + 5):(urlIndex + urlEndIndex)]), nil } ================================================ FILE: vendor/github.com/Masterminds/vcs/svn_test.go ================================================ package vcs import ( "io/ioutil" "path/filepath" "time" //"log" "os" "testing" ) // To verify svn is working we perform integration testing // with a known svn service. // Canary test to ensure SvnRepo implements the Repo interface. var _ Repo = &SvnRepo{} func TestSvn(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-svn-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewSvnRepo("https://github.com/Masterminds/VCSTestRepo/trunk", tempDir+string(os.PathSeparator)+"VCSTestRepo") if err != nil { t.Error(err) } if repo.Vcs() != Svn { t.Error("Svn is detecting the wrong type") } // Check the basic getters. if repo.Remote() != "https://github.com/Masterminds/VCSTestRepo/trunk" { t.Error("Remote not set properly") } if repo.LocalPath() != tempDir+string(os.PathSeparator)+"VCSTestRepo" { t.Error("Local disk location not set properly") } //Logger = log.New(os.Stdout, "", log.LstdFlags) // Do an initial checkout. err = repo.Get() if err != nil { t.Errorf("Unable to checkout SVN repo. Err was %s", err) } // Verify SVN repo is a SVN repo if !repo.CheckLocal() { t.Error("Problem checking out repo or SVN CheckLocal is not working") } // Verify an incorrect remote is caught when NewSvnRepo is used on an existing location _, nrerr := NewSvnRepo("https://github.com/Masterminds/VCSTestRepo/unknownbranch", tempDir+"/VCSTestRepo") if nrerr != ErrWrongRemote { t.Error("ErrWrongRemote was not triggered for SVN") } // Test internal lookup mechanism used outside of Hg specific functionality. ltype, err := DetectVcsFromFS(tempDir + "/VCSTestRepo") if err != nil { t.Error("detectVcsFromFS unable to Svn repo") } if ltype != Svn { t.Errorf("detectVcsFromFS detected %s instead of Svn type", ltype) } // Commenting out auto-detection tests for SVN. NewRepo automatically detects // GitHub to be a Git repo and that's an issue for this test. Need an // SVN host that can autodetect from before using this test again. // // Test NewRepo on existing checkout. This should simply provide a working // instance without error based on looking at the local directory. // nrepo, nrerr := NewRepo("https://github.com/Masterminds/VCSTestRepo/trunk", tempDir+"/VCSTestRepo") // if nrerr != nil { // t.Error(nrerr) // } // // Verify the right oject is returned. It will check the local repo type. // if nrepo.CheckLocal() == false { // t.Error("Wrong version returned from NewRepo") // } v, err := repo.Current() if err != nil { t.Errorf("Error trying Svn Current: %s", err) } if v != "HEAD" { t.Errorf("Current failed to detect Svn on HEAD. Got version: %s", v) } // Update the version to a previous version. err = repo.UpdateVersion("r2") if err != nil { t.Errorf("Unable to update SVN repo version. Err was %s", err) } // Use Version to verify we are on the right version. v, err = repo.Version() if v != "2" { t.Error("Error checking checked SVN out version") } if err != nil { t.Error(err) } v, err = repo.Current() if err != nil { t.Errorf("Error trying Svn Current for ref: %s", err) } if v != "2" { t.Errorf("Current failed to detect Svn on HEAD. Got version: %s", v) } // Perform an update which should take up back to the latest version. err = repo.Update() if err != nil { t.Error(err) } // Make sure we are on a newer version because of the update. v, err = repo.Version() if v == "2" { t.Error("Error with version. Still on old version. Update failed") } if err != nil { t.Error(err) } // Use Date to verify we are on the right commit. d, err := repo.Date() if d.Format(longForm) != "2015-07-29 13:47:03 +0000" { t.Error("Error checking checked out Svn commit date") } if err != nil { t.Error(err) } tags, err := repo.Tags() if err != nil { t.Error(err) } if len(tags) != 0 { t.Error("Svn is incorrectly returning tags") } tags, err = repo.TagsFromCommit("2") if err != nil { t.Error(err) } if len(tags) != 0 { t.Error("Svn is incorrectly returning tags for a commit") } branches, err := repo.Branches() if err != nil { t.Error(err) } if len(branches) != 0 { t.Error("Svn is incorrectly returning branches") } if !repo.IsReference("r4") { t.Error("Svn is reporting a reference is not one") } if repo.IsReference("55") { t.Error("Svn is reporting a non-existent reference is one") } if repo.IsDirty() { t.Error("Svn incorrectly reporting dirty") } ci, err := repo.CommitInfo("2") if err != nil { t.Error(err) } if ci.Commit != "2" { t.Error("Svn.CommitInfo wrong commit id") } if ci.Author != "matt.farina" { t.Error("Svn.CommitInfo wrong author") } if ci.Message != "Update README.md" { t.Error("Svn.CommitInfo wrong message") } ti, err := time.Parse(time.RFC3339Nano, "2015-07-29T13:46:20.000000Z") if err != nil { t.Error(err) } if !ti.Equal(ci.Date) { t.Error("Svn.CommitInfo wrong date") } _, err = repo.CommitInfo("555555555") if err != ErrRevisionUnavailable { t.Error("Svn didn't return expected ErrRevisionUnavailable") } tempDir2, err := ioutil.TempDir("", "go-vcs-svn-tests-export") if err != nil { t.Fatalf("Error creating temp directory: %s", err) } defer func() { err = os.RemoveAll(tempDir2) if err != nil { t.Error(err) } }() exportDir := filepath.Join(tempDir2, "src") err = repo.ExportDir(exportDir) if err != nil { t.Errorf("Unable to export Svn repo. Err was %s", err) } _, err = os.Stat(filepath.Join(exportDir, "README.md")) if err != nil { t.Errorf("Error checking exported file in Svn: %s", err) } _, err = os.Stat(filepath.Join(exportDir, string(repo.Vcs()))) if err != nil { if found := os.IsNotExist(err); !found { t.Errorf("Error checking exported metadata in Svn: %s", err) } } else { t.Error("Error checking Svn metadata. It exists.") } } func TestSvnCheckLocal(t *testing.T) { // Verify repo.CheckLocal fails for non-SVN directories. // TestSvn is already checking on a valid repo tempDir, err := ioutil.TempDir("", "go-vcs-svn-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, _ := NewSvnRepo("", tempDir) if repo.CheckLocal() { t.Error("SVN CheckLocal does not identify non-SVN location") } // Test NewRepo when there's no local. This should simply provide a working // instance without error based on looking at the remote localtion. _, nrerr := NewRepo("https://github.com/Masterminds/VCSTestRepo/trunk", tempDir+"/VCSTestRepo") if nrerr != nil { t.Error(nrerr) } } func TestSvnPing(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-svn-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewSvnRepo("https://github.com/Masterminds/VCSTestRepo/trunk", tempDir) if err != nil { t.Error(err) } ping := repo.Ping() if !ping { t.Error("Svn unable to ping working repo") } repo, err = NewSvnRepo("https://github.com/Masterminds/ihopethisneverexistsbecauseitshouldnt", tempDir) if err != nil { t.Error(err) } ping = repo.Ping() if ping { t.Error("Svn got a ping response from when it should not have") } } func TestSvnInit(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-svn-tests") remoteDir := tempDir + string(os.PathSeparator) + "remoteDir" localDir := tempDir + string(os.PathSeparator) + "localDir" if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() repo, err := NewSvnRepo(remoteDir, localDir) if err != nil { t.Error(err) } err = repo.Init() if err != nil { t.Error(err) } err = repo.Get() if err != nil { t.Error(err) } v, err := repo.Version() if err != nil { t.Error(err) } if v != "0" { t.Errorf("Svn Init returns wrong version: %s", v) } } ================================================ FILE: vendor/github.com/Masterminds/vcs/vcs_local_lookup.go ================================================ package vcs import ( "os" "runtime" "strings" ) // DetectVcsFromFS detects the type from the local path. // Is there a better way to do this? func DetectVcsFromFS(vcsPath string) (Type, error) { // There are cases under windows that a path could start with a / and it needs // to be stripped. For example, a path such as /C:\foio\bar. if runtime.GOOS == "windows" && strings.HasPrefix(vcsPath, "/") { vcsPath = strings.TrimPrefix(vcsPath, "/") } // When the local directory to the package doesn't exist // it's not yet downloaded so we can't detect the type // locally. if _, err := os.Stat(vcsPath); os.IsNotExist(err) { return "", ErrCannotDetectVCS } separator := string(os.PathSeparator) // Walk through each of the different VCS types to see if // one can be detected. Do this is order of guessed popularity. if _, err := os.Stat(vcsPath + separator + ".git"); err == nil { return Git, nil } if _, err := os.Stat(vcsPath + separator + ".svn"); err == nil { return Svn, nil } if _, err := os.Stat(vcsPath + separator + ".hg"); err == nil { return Hg, nil } if _, err := os.Stat(vcsPath + separator + ".bzr"); err == nil { return Bzr, nil } // If one was not already detected than we default to not finding it. return "", ErrCannotDetectVCS } ================================================ FILE: vendor/github.com/Masterminds/vcs/vcs_remote_lookup.go ================================================ package vcs import ( "encoding/json" "encoding/xml" "fmt" "io" "io/ioutil" "net/http" "net/url" "regexp" "strings" ) type vcsInfo struct { host string pattern string vcs Type addCheck func(m map[string]string, u *url.URL) (Type, error) regex *regexp.Regexp } // scpSyntaxRe matches the SCP-like addresses used by Git to access // repositories by SSH. var scpSyntaxRe = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) var vcsList = []*vcsInfo{ { host: "github.com", vcs: Git, pattern: `^(github\.com[/|:][A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`, }, { host: "bitbucket.org", pattern: `^(bitbucket\.org/(?P[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`, addCheck: checkBitbucket, }, { host: "launchpad.net", pattern: `^(launchpad\.net/(([A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`, vcs: Bzr, }, { host: "git.launchpad.net", vcs: Git, pattern: `^(git\.launchpad\.net/(([A-Za-z0-9_.\-]+)|~[A-Za-z0-9_.\-]+/(\+git|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))$`, }, { host: "hub.jazz.net", vcs: Git, pattern: `^(hub\.jazz\.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`, }, { host: "go.googlesource.com", vcs: Git, pattern: `^(go\.googlesource\.com/[A-Za-z0-9_.\-]+/?)$`, }, { host: "git.openstack.org", vcs: Git, pattern: `^(git\.openstack\.org/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)$`, }, // If none of the previous detect the type they will fall to this looking for the type in a generic sense // by the extension to the path. { addCheck: checkURL, pattern: `\.(?Pgit|hg|svn|bzr)$`, }, } func init() { // Precompile the regular expressions used to check VCS locations. for _, v := range vcsList { v.regex = regexp.MustCompile(v.pattern) } } // This function is really a hack around Go redirects rather than around // something VCS related. Should this be moved to the glide project or a // helper function? func detectVcsFromRemote(vcsURL string) (Type, string, error) { t, e := detectVcsFromURL(vcsURL) if e == nil { return t, vcsURL, nil } else if e != ErrCannotDetectVCS { return NoVCS, "", e } // Pages like https://golang.org/x/net provide an html document with // meta tags containing a location to work with. The go tool uses // a meta tag with the name go-import which is what we use here. // godoc.org also has one call go-source that we do not need to use. // The value of go-import is in the form "prefix vcs repo". The prefix // should match the vcsURL and the repo is a location that can be // checked out. Note, to get the html document you you need to add // ?go-get=1 to the url. u, err := url.Parse(vcsURL) if err != nil { return NoVCS, "", err } if u.RawQuery == "" { u.RawQuery = "go-get=1" } else { u.RawQuery = u.RawQuery + "+go-get=1" } checkURL := u.String() resp, err := http.Get(checkURL) if err != nil { return NoVCS, "", ErrCannotDetectVCS } defer resp.Body.Close() t, nu, err := parseImportFromBody(u, resp.Body) if err != nil { // TODO(mattfarina): Log the parsing error return NoVCS, "", ErrCannotDetectVCS } else if t == "" || nu == "" { return NoVCS, "", ErrCannotDetectVCS } return t, nu, nil } // From a remote vcs url attempt to detect the VCS. func detectVcsFromURL(vcsURL string) (Type, error) { var u *url.URL var err error if m := scpSyntaxRe.FindStringSubmatch(vcsURL); m != nil { // Match SCP-like syntax and convert it to a URL. // Eg, "git@github.com:user/repo" becomes // "ssh://git@github.com/user/repo". u = &url.URL{ Scheme: "ssh", User: url.User(m[1]), Host: m[2], Path: "/" + m[3], } } else { u, err = url.Parse(vcsURL) if err != nil { return "", err } } // Detect file schemes if u.Scheme == "file" { return DetectVcsFromFS(u.Path) } if u.Host == "" { return "", ErrCannotDetectVCS } // Try to detect from the scheme switch u.Scheme { case "git+ssh": return Git, nil case "git": return Git, nil case "bzr+ssh": return Bzr, nil case "svn+ssh": return Svn, nil } // Try to detect from known hosts, such as Github for _, v := range vcsList { if v.host != "" && v.host != u.Host { continue } // Make sure the pattern matches for an actual repo location. For example, // we should fail if the VCS listed is github.com/masterminds as that's // not actually a repo. uCheck := u.Host + u.Path m := v.regex.FindStringSubmatch(uCheck) if m == nil { if v.host != "" { return "", ErrCannotDetectVCS } continue } // If we are here the host matches. If the host has a singular // VCS type, such as Github, we can return the type right away. if v.vcs != "" { return v.vcs, nil } // Run additional checks to determine try and determine the repo // for the matched service. info := make(map[string]string) for i, name := range v.regex.SubexpNames() { if name != "" { info[name] = m[i] } } t, err := v.addCheck(info, u) if err != nil { switch err.(type) { case *RemoteError: return "", err } return "", ErrCannotDetectVCS } return t, nil } // Attempt to ascertain from the username passed in. if u.User != nil { un := u.User.Username() if un == "git" { return Git, nil } else if un == "hg" { return Hg, nil } } // Unable to determine the vcs from the url. return "", ErrCannotDetectVCS } // Figure out the type for Bitbucket by the passed in information // or via the public API. func checkBitbucket(i map[string]string, ul *url.URL) (Type, error) { // Fast path for ssh urls where we may not even be able to // anonymously get details from the API. if ul.User != nil { un := ul.User.Username() if un == "git" { return Git, nil } else if un == "hg" { return Hg, nil } } // The part of the response we care about. var response struct { SCM Type `json:"scm"` } u := expand(i, "https://api.bitbucket.org/2.0/repositories/{name}?fields=scm") data, err := get(u) if err != nil { return "", err } if err := json.Unmarshal(data, &response); err != nil { return "", fmt.Errorf("Decoding error %s: %v", u, err) } return response.SCM, nil } // Expect a type key on i with the exact type detected from the regex. func checkURL(i map[string]string, u *url.URL) (Type, error) { return Type(i["type"]), nil } func get(url string) ([]byte, error) { resp, err := http.Get(url) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { if resp.StatusCode == 404 { return nil, NewRemoteError("Not Found", err, resp.Status) } else if resp.StatusCode == 401 || resp.StatusCode == 403 { return nil, NewRemoteError("Access Denied", err, resp.Status) } return nil, fmt.Errorf("%s: %s", url, resp.Status) } b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("%s: %v", url, err) } return b, nil } func expand(match map[string]string, s string) string { for k, v := range match { s = strings.Replace(s, "{"+k+"}", v, -1) } return s } func parseImportFromBody(ur *url.URL, r io.ReadCloser) (tp Type, u string, err error) { d := xml.NewDecoder(r) d.CharsetReader = charsetReader d.Strict = false var t xml.Token for { t, err = d.Token() if err != nil { if err == io.EOF { // When the end is reached it could not detect a VCS if it // got here. err = ErrCannotDetectVCS } return } if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { return } if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { return } e, ok := t.(xml.StartElement) if !ok || !strings.EqualFold(e.Name.Local, "meta") { continue } if attrValue(e.Attr, "name") != "go-import" { continue } if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { // If the prefix supplied by the remote system isn't a prefix to the // url we're fetching continue to look for other imports. // This will work for exact matches and prefixes. For example, // golang.org/x/net as a prefix will match for golang.org/x/net and // golang.org/x/net/context. vcsURL := ur.Host + ur.Path if !strings.HasPrefix(vcsURL, f[0]) { continue } else { switch Type(f[1]) { case Git: tp = Git case Svn: tp = Svn case Bzr: tp = Bzr case Hg: tp = Hg } u = f[2] return } } } } func charsetReader(charset string, input io.Reader) (io.Reader, error) { switch strings.ToLower(charset) { case "ascii": return input, nil default: return nil, fmt.Errorf("can't decode XML document using charset %q", charset) } } func attrValue(attrs []xml.Attr, name string) string { for _, a := range attrs { if strings.EqualFold(a.Name.Local, name) { return a.Value } } return "" } ================================================ FILE: vendor/github.com/Masterminds/vcs/vcs_remote_lookup_test.go ================================================ package vcs import ( "io/ioutil" "os" "os/exec" "runtime" "strings" "testing" ) func TestVCSLookup(t *testing.T) { // TODO: Expand to make sure it detected the right vcs. urlList := map[string]struct { work bool t Type }{ "https://github.com/masterminds": {work: false, t: Git}, "https://github.com/Masterminds/VCSTestRepo": {work: true, t: Git}, "https://bitbucket.org/mattfarina/testhgrepo": {work: true, t: Hg}, "https://bitbucket.org/mattfarina/repo-does-not-exist": {work: false, t: Hg}, "https://bitbucket.org/mattfarina/private-repo-for-vcs-testing": {work: false, t: Hg}, "https://launchpad.net/govcstestbzrrepo/trunk": {work: true, t: Bzr}, "https://launchpad.net/~mattfarina/+junk/mygovcstestbzrrepo": {work: true, t: Bzr}, "https://launchpad.net/~mattfarina/+junk/mygovcstestbzrrepo/trunk": {work: true, t: Bzr}, "https://git.launchpad.net/govcstestgitrepo": {work: true, t: Git}, "https://git.launchpad.net/~mattfarina/+git/mygovcstestgitrepo": {work: true, t: Git}, "https://hub.jazz.net/git/user1/pkgname": {work: true, t: Git}, "https://hub.jazz.net/git/user1/pkgname/subpkg/subpkg/subpkg": {work: true, t: Git}, "https://hubs.jazz.net/git/user1/pkgname": {work: false, t: Git}, "https://example.com/foo/bar.git": {work: true, t: Git}, "https://example.com/foo/bar.svn": {work: true, t: Svn}, "https://example.com/foo/bar/baz.bzr": {work: true, t: Bzr}, "https://example.com/foo/bar/baz.hg": {work: true, t: Hg}, "https://gopkg.in/tomb.v1": {work: true, t: Git}, "https://golang.org/x/net": {work: true, t: Git}, "https://git.openstack.org/foo/bar": {work: true, t: Git}, "git@github.com:Masterminds/vcs.git": {work: true, t: Git}, "git@example.com:foo.git": {work: true, t: Git}, "ssh://hg@bitbucket.org/mattfarina/testhgrepo": {work: true, t: Hg}, "git@bitbucket.org:mattfarina/glide-bitbucket-example.git": {work: true, t: Git}, "git+ssh://example.com/foo/bar": {work: true, t: Git}, "git://example.com/foo/bar": {work: true, t: Git}, "bzr+ssh://example.com/foo/bar": {work: true, t: Bzr}, "svn+ssh://example.com/foo/bar": {work: true, t: Svn}, "git@example.com:foo/bar": {work: true, t: Git}, "hg@example.com:foo/bar": {work: true, t: Hg}, } for u, c := range urlList { ty, _, err := detectVcsFromRemote(u) if err == nil && !c.work { t.Errorf("Error detecting VCS from URL(%s)", u) } if err == ErrCannotDetectVCS && c.work { t.Errorf("Error detecting VCS from URL(%s)", u) } if err != nil && c.work { t.Errorf("Error detecting VCS from URL(%s): %s", u, err) } if err != nil && err != ErrCannotDetectVCS && !strings.HasSuffix(err.Error(), "Not Found") && !strings.HasSuffix(err.Error(), "Access Denied") && !c.work { t.Errorf("Unexpected error returned (%s): %s", u, err) } if c.work && ty != c.t { t.Errorf("Incorrect VCS type returned(%s)", u) } } } func TestVCSFileLookup(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-file-lookup-tests") if err != nil { t.Error(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() _, err = exec.Command("git", "init", tempDir).CombinedOutput() if err != nil { t.Error(err) } // On Windows it should be file:// followed by /C:\for\bar. That / before // the drive needs to be included in testing. var pth string if runtime.GOOS == "windows" { pth = "file:///" + tempDir } else { pth = "file://" + tempDir } ty, _, err := detectVcsFromRemote(pth) if err != nil { t.Errorf("Unable to detect file:// path: %s", err) } if ty != Git { t.Errorf("Detected wrong type from file:// path. Found type %v", ty) } } ================================================ FILE: vendor/github.com/codegangsta/cli/.flake8 ================================================ [flake8] max-line-length = 120 ================================================ FILE: vendor/github.com/codegangsta/cli/.gitignore ================================================ *.coverprofile node_modules/ ================================================ FILE: vendor/github.com/codegangsta/cli/.travis.yml ================================================ language: go sudo: false dist: trusty osx_image: xcode8.3 go: 1.8.x os: - linux - osx cache: directories: - node_modules before_script: - go get github.com/urfave/gfmrun/... || true - go get golang.org/x/tools/cmd/goimports - if [ ! -f node_modules/.bin/markdown-toc ] ; then npm install markdown-toc ; fi script: - ./runtests gen - ./runtests vet - ./runtests test - ./runtests gfmrun - ./runtests toc ================================================ FILE: vendor/github.com/codegangsta/cli/CHANGELOG.md ================================================ # Change Log **ATTN**: This project uses [semantic versioning](http://semver.org/). ## [Unreleased] ## 1.20.0 - 2017-08-10 ### Fixed * `HandleExitCoder` is now correctly iterates over all errors in a `MultiError`. The exit code is the exit code of the last error or `1` if there are no `ExitCoder`s in the `MultiError`. * Fixed YAML file loading on Windows (previously would fail validate the file path) * Subcommand `Usage`, `Description`, `ArgsUsage`, `OnUsageError` correctly propogated * `ErrWriter` is now passed downwards through command structure to avoid the need to redefine it * Pass `Command` context into `OnUsageError` rather than parent context so that all fields are avaiable * Errors occuring in `Before` funcs are no longer double printed * Use `UsageText` in the help templates for commands and subcommands if defined; otherwise build the usage as before (was previously ignoring this field) * `IsSet` and `GlobalIsSet` now correctly return whether a flag is set if a program calls `Set` or `GlobalSet` directly after flag parsing (would previously only return `true` if the flag was set during parsing) ### Changed * No longer exit the program on command/subcommand error if the error raised is not an `OsExiter`. This exiting behavior was introduced in 1.19.0, but was determined to be a regression in functionality. See [the PR](https://github.com/urfave/cli/pull/595) for discussion. ### Added * `CommandsByName` type was added to make it easy to sort `Command`s by name, alphabetically * `altsrc` now handles loading of string and int arrays from TOML * Support for definition of custom help templates for `App` via `CustomAppHelpTemplate` * Support for arbitrary key/value fields on `App` to be used with `CustomAppHelpTemplate` via `ExtraInfo` * `HelpFlag`, `VersionFlag`, and `BashCompletionFlag` changed to explictly be `cli.Flag`s allowing for the use of custom flags satisfying the `cli.Flag` interface to be used. ## [1.19.1] - 2016-11-21 ### Fixed - Fixes regression introduced in 1.19.0 where using an `ActionFunc` as the `Action` for a command would cause it to error rather than calling the function. Should not have a affected declarative cases using `func(c *cli.Context) err)`. - Shell completion now handles the case where the user specifies `--generate-bash-completion` immediately after a flag that takes an argument. Previously it call the application with `--generate-bash-completion` as the flag value. ## [1.19.0] - 2016-11-19 ### Added - `FlagsByName` was added to make it easy to sort flags (e.g. `sort.Sort(cli.FlagsByName(app.Flags))`) - A `Description` field was added to `App` for a more detailed description of the application (similar to the existing `Description` field on `Command`) - Flag type code generation via `go generate` - Write to stderr and exit 1 if action returns non-nil error - Added support for TOML to the `altsrc` loader - `SkipArgReorder` was added to allow users to skip the argument reordering. This is useful if you want to consider all "flags" after an argument as arguments rather than flags (the default behavior of the stdlib `flag` library). This is backported functionality from the [removal of the flag reordering](https://github.com/urfave/cli/pull/398) in the unreleased version 2 - For formatted errors (those implementing `ErrorFormatter`), the errors will be formatted during output. Compatible with `pkg/errors`. ### Changed - Raise minimum tested/supported Go version to 1.2+ ### Fixed - Consider empty environment variables as set (previously environment variables with the equivalent of `""` would be skipped rather than their value used). - Return an error if the value in a given environment variable cannot be parsed as the flag type. Previously these errors were silently swallowed. - Print full error when an invalid flag is specified (which includes the invalid flag) - `App.Writer` defaults to `stdout` when `nil` - If no action is specified on a command or app, the help is now printed instead of `panic`ing - `App.Metadata` is initialized automatically now (previously was `nil` unless initialized) - Correctly show help message if `-h` is provided to a subcommand - `context.(Global)IsSet` now respects environment variables. Previously it would return `false` if a flag was specified in the environment rather than as an argument - Removed deprecation warnings to STDERR to avoid them leaking to the end-user - `altsrc`s import paths were updated to use `gopkg.in/urfave/cli.v1`. This fixes issues that occurred when `gopkg.in/urfave/cli.v1` was imported as well as `altsrc` where Go would complain that the types didn't match ## [1.18.1] - 2016-08-28 ### Fixed - Removed deprecation warnings to STDERR to avoid them leaking to the end-user (backported) ## [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.1] - 2016-08-28 ### Fixed - Removed deprecation warnings to STDERR to avoid them leaking to the end-user ## [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.1] - 2016-08-28 ### Fixed - Removed deprecation warnings to STDERR to avoid them leaking to the end-user ## [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 - `cli.App.RunAndExitOnError`, which should now be done by returning an error that fulfills `cli.ExitCoder` to `cli.App.Run`. - 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 ================================================ FILE: vendor/github.com/codegangsta/cli/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: 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. ================================================ FILE: vendor/github.com/codegangsta/cli/README.md ================================================ cli === [![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli) [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) [![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli) [![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli) [![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) / [![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc) **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. - [Overview](#overview) - [Installation](#installation) * [Supported platforms](#supported-platforms) * [Using the `v2` branch](#using-the-v2-branch) * [Pinning to the `v1` releases](#pinning-to-the-v1-releases) - [Getting Started](#getting-started) - [Examples](#examples) * [Arguments](#arguments) * [Flags](#flags) + [Placeholder Values](#placeholder-values) + [Alternate Names](#alternate-names) + [Ordering](#ordering) + [Values from the Environment](#values-from-the-environment) + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-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) ## 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. **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 version 1.2+ is supported. [See the install instructions for Go](http://golang.org/doc/install.html). To install cli, simply run: ``` $ go get github.com/urfave/cli ``` Make sure your `PATH` includes 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` releases 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 `v1` is an acceptable option, e.g.: ``` $ go get gopkg.in/urfave/cli.v1 ``` ``` go ... import ( "gopkg.in/urfave/cli.v1" // imports as package "cli" ) ... ``` This will pull the latest tagged `v1` release (e.g. `v1.18.1` at the time of writing). ## Getting Started 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()`. ``` go package main import ( "os" "github.com/urfave/cli" ) func main() { cli.NewApp().Run(os.Args) } ``` This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: ``` go package main import ( "fmt" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Name = "boom" app.Usage = "make an explosive entrance" 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. ## 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! Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it: ``` go package main import ( "fmt" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Name = "greet" app.Usage = "fight the loneliness!" app.Action = func(c *cli.Context) error { fmt.Println("Hello friend!") return nil } app.Run(os.Args) } ``` Install our command to the `$GOPATH/bin` directory: ``` $ go install ``` Finally run our new command: ``` $ greet Hello friend! ``` cli also generates neat help text: ``` $ greet help NAME: greet - fight the loneliness! USAGE: greet [global options] command [command options] [arguments...] VERSION: 0.0.0 COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS --version Shows version information ``` ### Arguments You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.: ``` go 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. ``` go 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", }, } 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. ``` go 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, }, } 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/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: ```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. ``` go 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. #### Ordering Flags for the application and commands are shown in the order they are defined. However, it's possible to sort them from outside this library by using `FlagsByName` or `CommandsByName` with `sort`. For example this: ``` go package main import ( "os" "sort" "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", }, cli.StringFlag{ Name: "config, c", Usage: "Load configuration from `FILE`", }, } app.Commands = []cli.Command{ { Name: "complete", Aliases: []string{"c"}, Usage: "complete a task on the list", Action: func(c *cli.Context) error { return nil }, }, { Name: "add", Aliases: []string{"a"}, Usage: "add a task to the list", Action: func(c *cli.Context) error { return nil }, }, } sort.Sort(cli.FlagsByName(app.Flags)) sort.Sort(cli.CommandsByName(app.Commands)) app.Run(os.Args) } ``` Will result in help output like: ``` --config FILE, -c FILE Load configuration from FILE --lang value, -l value Language for the greeting (default: "english") ``` #### Values from the Environment You can also have the default value set from the environment via `EnvVar`. e.g. ``` go 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. ``` go 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, TOML, and others) There is a separate package altsrc that adds support for getting flag values from other file input sources. Currently supported input source formats: * YAML * TOML 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. ``` 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. Currently only the aboved specified formats 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: ``` go 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. ```go 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: "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 For additional organization in apps that have many subcommands, you can associate a category for each command to group them together in the help output. E.g. ```go 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 You can enable completion commands by setting the `EnableBashCompletion` flag on the `App` object. By default, this setting will only auto-complete to show an app's subcommands, but you can write your own completion methods for the App or its subcommands. ``` 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) } ``` #### 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` #### 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 automatically install it there if you are distributing a package). Don't forget to source the file to make it active in the current shell. ``` sudo cp src/bash_autocomplete /etc/bash_completion.d/ source /etc/bash_completion.d/ ``` Alternatively, you can just document that users should source the generic `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.: ``` 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.: ``` 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: {{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.: ``` 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 customized to something other than `-v/--version` by setting `cli.VersionFlag`, e.g.: ``` 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 = "19.99.0" app.Run(os.Args) } ``` Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.: ``` 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 = "19.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. ``` 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 = "19.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. 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, we probably forgot to add you, please open an issue. ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/altsrc.go ================================================ package altsrc //go:generate python ../generate-flag-types altsrc -i ../flag-types.json -o flag_generated.go ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/flag.go ================================================ package altsrc import ( "fmt" "strconv" "strings" "syscall" "gopkg.in/urfave/cli.v1" ) // FlagInputSourceExtension is an extension interface of cli.Flag that // allows a value to be set on the existing parsed flags. type FlagInputSourceExtension interface { cli.Flag ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error } // ApplyInputSourceValues iterates over all provided flags and // executes ApplyInputSourceValue on flags implementing the // FlagInputSourceExtension interface to initialize these flags // to an alternate input source. func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error { for _, f := range flags { inputSourceExtendedFlag, isType := f.(FlagInputSourceExtension) if isType { err := inputSourceExtendedFlag.ApplyInputSourceValue(context, inputSourceContext) if err != nil { return err } } } return nil } // 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)) cli.BeforeFunc { return func(context *cli.Context) error { inputSource, err := createInputSource() if err != nil { return fmt.Errorf("Unable to create input source: inner error: \n'%v'", err.Error()) } return ApplyInputSourceValues(context, inputSource, flags) } } // 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)) cli.BeforeFunc { return func(context *cli.Context) error { inputSource, err := createInputSource(context) if err != nil { return fmt.Errorf("Unable to create input source with context: inner error: \n'%v'", err.Error()) } return ApplyInputSourceValues(context, inputSource, flags) } } // ApplyInputSourceValue applies a generic value to the flagSet if required func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { value, err := isc.Generic(f.GenericFlag.Name) if err != nil { return err } if value != nil { eachName(f.Name, func(name string) { f.set.Set(f.Name, value.String()) }) } } } return nil } // ApplyInputSourceValue applies a StringSlice value to the flagSet if required func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { value, err := isc.StringSlice(f.StringSliceFlag.Name) if err != nil { return err } if value != nil { var sliceValue cli.StringSlice = value eachName(f.Name, func(name string) { underlyingFlag := f.set.Lookup(f.Name) if underlyingFlag != nil { underlyingFlag.Value = &sliceValue } }) } } } return nil } // ApplyInputSourceValue applies a IntSlice value if required func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { value, err := isc.IntSlice(f.IntSliceFlag.Name) if err != nil { return err } if value != nil { var sliceValue cli.IntSlice = value eachName(f.Name, func(name string) { underlyingFlag := f.set.Lookup(f.Name) if underlyingFlag != nil { underlyingFlag.Value = &sliceValue } }) } } } return nil } // ApplyInputSourceValue applies a Bool value to the flagSet if required func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { value, err := isc.Bool(f.BoolFlag.Name) if err != nil { return err } if value { eachName(f.Name, func(name string) { f.set.Set(f.Name, strconv.FormatBool(value)) }) } } } return nil } // ApplyInputSourceValue applies a BoolT value to the flagSet if required func (f *BoolTFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { value, err := isc.BoolT(f.BoolTFlag.Name) if err != nil { return err } if !value { eachName(f.Name, func(name string) { f.set.Set(f.Name, strconv.FormatBool(value)) }) } } } return nil } // ApplyInputSourceValue applies a String value to the flagSet if required func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { value, err := isc.String(f.StringFlag.Name) if err != nil { return err } if value != "" { eachName(f.Name, func(name string) { f.set.Set(f.Name, value) }) } } } return nil } // ApplyInputSourceValue applies a int value to the flagSet if required func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { value, err := isc.Int(f.IntFlag.Name) if err != nil { return err } if value > 0 { eachName(f.Name, func(name string) { f.set.Set(f.Name, strconv.FormatInt(int64(value), 10)) }) } } } return nil } // ApplyInputSourceValue applies a Duration value to the flagSet if required func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { value, err := isc.Duration(f.DurationFlag.Name) if err != nil { return err } if value > 0 { eachName(f.Name, func(name string) { f.set.Set(f.Name, value.String()) }) } } } return nil } // ApplyInputSourceValue applies a Float64 value to the flagSet if required func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { value, err := isc.Float64(f.Float64Flag.Name) if err != nil { return err } if value > 0 { floatStr := float64ToString(value) eachName(f.Name, func(name string) { f.set.Set(f.Name, floatStr) }) } } } return nil } func isEnvVarSet(envVars string) bool { for _, envVar := range strings.Split(envVars, ",") { envVar = strings.TrimSpace(envVar) if _, ok := syscall.Getenv(envVar); ok { // TODO: Can't use this for bools as // set means that it was true or false based on // Bool flag type, should work for other types return true } } return false } func float64ToString(f float64) string { return fmt.Sprintf("%v", f) } func eachName(longName string, fn func(string)) { parts := strings.Split(longName, ",") for _, name := range parts { name = strings.Trim(name, " ") fn(name) } } ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/flag_generated.go ================================================ package altsrc import ( "flag" "gopkg.in/urfave/cli.v1" ) // WARNING: This file is generated! // BoolFlag is the flag type that wraps cli.BoolFlag to allow // for other values to be specified type BoolFlag struct { cli.BoolFlag set *flag.FlagSet } // NewBoolFlag creates a new BoolFlag func NewBoolFlag(fl cli.BoolFlag) *BoolFlag { return &BoolFlag{BoolFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped BoolFlag.Apply func (f *BoolFlag) Apply(set *flag.FlagSet) { f.set = set f.BoolFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped BoolFlag.ApplyWithError func (f *BoolFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.BoolFlag.ApplyWithError(set) } // BoolTFlag is the flag type that wraps cli.BoolTFlag to allow // for other values to be specified type BoolTFlag struct { cli.BoolTFlag set *flag.FlagSet } // NewBoolTFlag creates a new BoolTFlag func NewBoolTFlag(fl cli.BoolTFlag) *BoolTFlag { return &BoolTFlag{BoolTFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped BoolTFlag.Apply func (f *BoolTFlag) Apply(set *flag.FlagSet) { f.set = set f.BoolTFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped BoolTFlag.ApplyWithError func (f *BoolTFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.BoolTFlag.ApplyWithError(set) } // DurationFlag is the flag type that wraps cli.DurationFlag to allow // for other values to be specified type DurationFlag struct { cli.DurationFlag set *flag.FlagSet } // NewDurationFlag creates a new DurationFlag func NewDurationFlag(fl cli.DurationFlag) *DurationFlag { return &DurationFlag{DurationFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped DurationFlag.Apply func (f *DurationFlag) Apply(set *flag.FlagSet) { f.set = set f.DurationFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped DurationFlag.ApplyWithError func (f *DurationFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.DurationFlag.ApplyWithError(set) } // Float64Flag is the flag type that wraps cli.Float64Flag to allow // for other values to be specified type Float64Flag struct { cli.Float64Flag set *flag.FlagSet } // NewFloat64Flag creates a new Float64Flag func NewFloat64Flag(fl cli.Float64Flag) *Float64Flag { return &Float64Flag{Float64Flag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped Float64Flag.Apply func (f *Float64Flag) Apply(set *flag.FlagSet) { f.set = set f.Float64Flag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped Float64Flag.ApplyWithError func (f *Float64Flag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Float64Flag.ApplyWithError(set) } // GenericFlag is the flag type that wraps cli.GenericFlag to allow // for other values to be specified type GenericFlag struct { cli.GenericFlag set *flag.FlagSet } // NewGenericFlag creates a new GenericFlag func NewGenericFlag(fl cli.GenericFlag) *GenericFlag { return &GenericFlag{GenericFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped GenericFlag.Apply func (f *GenericFlag) Apply(set *flag.FlagSet) { f.set = set f.GenericFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped GenericFlag.ApplyWithError func (f *GenericFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.GenericFlag.ApplyWithError(set) } // Int64Flag is the flag type that wraps cli.Int64Flag to allow // for other values to be specified type Int64Flag struct { cli.Int64Flag set *flag.FlagSet } // NewInt64Flag creates a new Int64Flag func NewInt64Flag(fl cli.Int64Flag) *Int64Flag { return &Int64Flag{Int64Flag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped Int64Flag.Apply func (f *Int64Flag) Apply(set *flag.FlagSet) { f.set = set f.Int64Flag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped Int64Flag.ApplyWithError func (f *Int64Flag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Int64Flag.ApplyWithError(set) } // IntFlag is the flag type that wraps cli.IntFlag to allow // for other values to be specified type IntFlag struct { cli.IntFlag set *flag.FlagSet } // NewIntFlag creates a new IntFlag func NewIntFlag(fl cli.IntFlag) *IntFlag { return &IntFlag{IntFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped IntFlag.Apply func (f *IntFlag) Apply(set *flag.FlagSet) { f.set = set f.IntFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped IntFlag.ApplyWithError func (f *IntFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.IntFlag.ApplyWithError(set) } // IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow // for other values to be specified type IntSliceFlag struct { cli.IntSliceFlag set *flag.FlagSet } // NewIntSliceFlag creates a new IntSliceFlag func NewIntSliceFlag(fl cli.IntSliceFlag) *IntSliceFlag { return &IntSliceFlag{IntSliceFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped IntSliceFlag.Apply func (f *IntSliceFlag) Apply(set *flag.FlagSet) { f.set = set f.IntSliceFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped IntSliceFlag.ApplyWithError func (f *IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.IntSliceFlag.ApplyWithError(set) } // Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow // for other values to be specified type Int64SliceFlag struct { cli.Int64SliceFlag set *flag.FlagSet } // NewInt64SliceFlag creates a new Int64SliceFlag func NewInt64SliceFlag(fl cli.Int64SliceFlag) *Int64SliceFlag { return &Int64SliceFlag{Int64SliceFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped Int64SliceFlag.Apply func (f *Int64SliceFlag) Apply(set *flag.FlagSet) { f.set = set f.Int64SliceFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped Int64SliceFlag.ApplyWithError func (f *Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Int64SliceFlag.ApplyWithError(set) } // StringFlag is the flag type that wraps cli.StringFlag to allow // for other values to be specified type StringFlag struct { cli.StringFlag set *flag.FlagSet } // NewStringFlag creates a new StringFlag func NewStringFlag(fl cli.StringFlag) *StringFlag { return &StringFlag{StringFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped StringFlag.Apply func (f *StringFlag) Apply(set *flag.FlagSet) { f.set = set f.StringFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped StringFlag.ApplyWithError func (f *StringFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.StringFlag.ApplyWithError(set) } // StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow // for other values to be specified type StringSliceFlag struct { cli.StringSliceFlag set *flag.FlagSet } // NewStringSliceFlag creates a new StringSliceFlag func NewStringSliceFlag(fl cli.StringSliceFlag) *StringSliceFlag { return &StringSliceFlag{StringSliceFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped StringSliceFlag.Apply func (f *StringSliceFlag) Apply(set *flag.FlagSet) { f.set = set f.StringSliceFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped StringSliceFlag.ApplyWithError func (f *StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.StringSliceFlag.ApplyWithError(set) } // Uint64Flag is the flag type that wraps cli.Uint64Flag to allow // for other values to be specified type Uint64Flag struct { cli.Uint64Flag set *flag.FlagSet } // NewUint64Flag creates a new Uint64Flag func NewUint64Flag(fl cli.Uint64Flag) *Uint64Flag { return &Uint64Flag{Uint64Flag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped Uint64Flag.Apply func (f *Uint64Flag) Apply(set *flag.FlagSet) { f.set = set f.Uint64Flag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped Uint64Flag.ApplyWithError func (f *Uint64Flag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Uint64Flag.ApplyWithError(set) } // UintFlag is the flag type that wraps cli.UintFlag to allow // for other values to be specified type UintFlag struct { cli.UintFlag set *flag.FlagSet } // NewUintFlag creates a new UintFlag func NewUintFlag(fl cli.UintFlag) *UintFlag { return &UintFlag{UintFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls the // wrapped UintFlag.Apply func (f *UintFlag) Apply(set *flag.FlagSet) { f.set = set f.UintFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped UintFlag.ApplyWithError func (f *UintFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.UintFlag.ApplyWithError(set) } ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/flag_test.go ================================================ package altsrc import ( "flag" "fmt" "os" "strings" "testing" "time" "gopkg.in/urfave/cli.v1" ) type testApplyInputSource struct { Flag FlagInputSourceExtension FlagName string FlagSetName string Expected string ContextValueString string ContextValue flag.Value EnvVarValue string EnvVarName string MapValue interface{} } func TestGenericApplyInputSourceValue(t *testing.T) { v := &Parser{"abc", "def"} c := runTest(t, testApplyInputSource{ Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}}), FlagName: "test", MapValue: v, }) expect(t, v, c.Generic("test")) } func TestGenericApplyInputSourceMethodContextSet(t *testing.T) { p := &Parser{"abc", "def"} c := runTest(t, testApplyInputSource{ Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}}), FlagName: "test", MapValue: &Parser{"efg", "hig"}, ContextValueString: p.String(), }) expect(t, p, c.Generic("test")) } func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}, EnvVar: "TEST"}), FlagName: "test", MapValue: &Parser{"efg", "hij"}, EnvVarName: "TEST", EnvVarValue: "abc,def", }) expect(t, &Parser{"abc", "def"}, c.Generic("test")) } func TestStringSliceApplyInputSourceValue(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{"hello", "world"}, }) expect(t, c.StringSlice("test"), []string{"hello", "world"}) } func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{"hello", "world"}, ContextValueString: "ohno", }) expect(t, c.StringSlice("test"), []string{"ohno"}) } func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: []interface{}{"hello", "world"}, EnvVarName: "TEST", EnvVarValue: "oh,no", }) expect(t, c.StringSlice("test"), []string{"oh", "no"}) } func TestIntSliceApplyInputSourceValue(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{1, 2}, }) expect(t, c.IntSlice("test"), []int{1, 2}) } func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{1, 2}, ContextValueString: "3", }) expect(t, c.IntSlice("test"), []int{3}) } func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: []interface{}{1, 2}, EnvVarName: "TEST", EnvVarValue: "3,4", }) expect(t, c.IntSlice("test"), []int{3, 4}) } func TestBoolApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolFlag(cli.BoolFlag{Name: "test"}), FlagName: "test", MapValue: true, }) expect(t, true, c.Bool("test")) } func TestBoolApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolFlag(cli.BoolFlag{Name: "test"}), FlagName: "test", MapValue: false, ContextValueString: "true", }) expect(t, true, c.Bool("test")) } func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolFlag(cli.BoolFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: false, EnvVarName: "TEST", EnvVarValue: "true", }) expect(t, true, c.Bool("test")) } func TestBoolTApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test"}), FlagName: "test", MapValue: false, }) expect(t, false, c.BoolT("test")) } func TestBoolTApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test"}), FlagName: "test", MapValue: true, ContextValueString: "false", }) expect(t, false, c.BoolT("test")) } func TestBoolTApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: true, EnvVarName: "TEST", EnvVarValue: "false", }) expect(t, false, c.BoolT("test")) } func TestStringApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringFlag(cli.StringFlag{Name: "test"}), FlagName: "test", MapValue: "hello", }) expect(t, "hello", c.String("test")) } func TestStringApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringFlag(cli.StringFlag{Name: "test"}), FlagName: "test", MapValue: "hello", ContextValueString: "goodbye", }) expect(t, "goodbye", c.String("test")) } func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringFlag(cli.StringFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: "hello", EnvVarName: "TEST", EnvVarValue: "goodbye", }) expect(t, "goodbye", c.String("test")) } func TestIntApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntFlag(cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: 15, }) expect(t, 15, c.Int("test")) } func TestIntApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntFlag(cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: 15, ContextValueString: "7", }) expect(t, 7, c.Int("test")) } func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: 15, EnvVarName: "TEST", EnvVarValue: "12", }) expect(t, 12, c.Int("test")) } func TestDurationApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: time.Duration(30 * time.Second), }) expect(t, time.Duration(30*time.Second), c.Duration("test")) } func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: time.Duration(30 * time.Second), ContextValueString: time.Duration(15 * time.Second).String(), }) expect(t, time.Duration(15*time.Second), c.Duration("test")) } func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(cli.DurationFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: time.Duration(30 * time.Second), EnvVarName: "TEST", EnvVarValue: time.Duration(15 * time.Second).String(), }) expect(t, time.Duration(15*time.Second), c.Duration("test")) } func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewFloat64Flag(cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: 1.3, }) expect(t, 1.3, c.Float64("test")) } func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewFloat64Flag(cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: 1.3, ContextValueString: fmt.Sprintf("%v", 1.4), }) expect(t, 1.4, c.Float64("test")) } func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewFloat64Flag(cli.Float64Flag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: 1.3, EnvVarName: "TEST", EnvVarValue: fmt.Sprintf("%v", 1.4), }) expect(t, 1.4, c.Float64("test")) } func runTest(t *testing.T, test testApplyInputSource) *cli.Context { 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 != "" { os.Setenv(test.EnvVarName, test.EnvVarValue) defer os.Setenv(test.EnvVarName, "") } test.Flag.Apply(set) if test.ContextValue != nil { flag := set.Lookup(test.FlagName) flag.Value = test.ContextValue } if test.ContextValueString != "" { set.Set(test.FlagName, test.ContextValueString) } test.Flag.ApplyInputSourceValue(c, inputSource) return c } type Parser [2]string func (p *Parser) Set(value string) error { parts := strings.Split(value, ",") if len(parts) != 2 { return fmt.Errorf("invalid format") } (*p)[0] = parts[0] (*p)[1] = parts[1] return nil } func (p *Parser) String() string { return fmt.Sprintf("%s,%s", p[0], p[1]) } ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/helpers_test.go ================================================ package altsrc import ( "reflect" "testing" ) func expect(t *testing.T, a interface{}, b interface{}) { if !reflect.DeepEqual(b, a) { t.Errorf("Expected %#v (type %v) - Got %#v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) } } func refute(t *testing.T, a interface{}, b interface{}) { if a == b { t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) } } ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/input_source_context.go ================================================ package altsrc import ( "time" "gopkg.in/urfave/cli.v1" ) // InputSourceContext is an interface used to allow // other input sources to be implemented as needed. type InputSourceContext interface { Int(name string) (int, error) Duration(name string) (time.Duration, error) Float64(name string) (float64, error) String(name string) (string, error) StringSlice(name string) ([]string, error) IntSlice(name string) ([]int, error) Generic(name string) (cli.Generic, error) Bool(name string) (bool, error) BoolT(name string) (bool, error) } ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/map_input_source.go ================================================ package altsrc import ( "fmt" "reflect" "strings" "time" "gopkg.in/urfave/cli.v1" ) // MapInputSource implements InputSourceContext to return // data from the map that is loaded. type MapInputSource struct { 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 func (fsm *MapInputSource) Int(name string) (int, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(int) 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 } return 0, nil } // Duration returns a duration from the map if it exists otherwise returns 0 func (fsm *MapInputSource) Duration(name string) (time.Duration, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(time.Duration) if !isType { return 0, incorrectTypeForFlagError(name, "duration", otherGenericValue) } 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 } // Float64 returns an float64 from the map if it exists otherwise returns 0 func (fsm *MapInputSource) Float64(name string) (float64, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(float64) if !isType { return 0, incorrectTypeForFlagError(name, "float64", otherGenericValue) } 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 } // String returns a string from the map if it exists otherwise returns an empty string func (fsm *MapInputSource) String(name string) (string, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(string) if !isType { return "", incorrectTypeForFlagError(name, "string", otherGenericValue) } 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 } // StringSlice returns an []string from the map if it exists otherwise returns nil func (fsm *MapInputSource) StringSlice(name string) ([]string, error) { otherGenericValue, exists := fsm.valueMap[name] if !exists { otherGenericValue, exists = nestedVal(name, fsm.valueMap) if !exists { return nil, nil } } otherValue, isType := otherGenericValue.([]interface{}) if !isType { return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue) } var stringSlice = make([]string, 0, len(otherValue)) for i, v := range otherValue { stringValue, isType := v.(string) if !isType { return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "string", v) } stringSlice = append(stringSlice, stringValue) } return stringSlice, nil } // IntSlice returns an []int from the map if it exists otherwise returns nil func (fsm *MapInputSource) IntSlice(name string) ([]int, error) { otherGenericValue, exists := fsm.valueMap[name] if !exists { otherGenericValue, exists = nestedVal(name, fsm.valueMap) if !exists { return nil, nil } } otherValue, isType := otherGenericValue.([]interface{}) if !isType { return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue) } var intSlice = make([]int, 0, len(otherValue)) for i, v := range otherValue { intValue, isType := v.(int) if !isType { return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "int", v) } intSlice = append(intSlice, intValue) } return intSlice, nil } // Generic returns an cli.Generic from the map if it exists otherwise returns nil func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(cli.Generic) if !isType { return nil, incorrectTypeForFlagError(name, "cli.Generic", otherGenericValue) } 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 } // Bool returns an bool from the map otherwise returns false func (fsm *MapInputSource) Bool(name string) (bool, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(bool) if !isType { return false, incorrectTypeForFlagError(name, "bool", otherGenericValue) } 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 } // BoolT returns an bool from the map otherwise returns true func (fsm *MapInputSource) BoolT(name string) (bool, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(bool) if !isType { return true, incorrectTypeForFlagError(name, "bool", otherGenericValue) } 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 } func incorrectTypeForFlagError(name, expectedTypeName string, value interface{}) error { valueType := reflect.TypeOf(value) valueTypeName := "" if valueType != nil { valueTypeName = valueType.Name() } return fmt.Errorf("Mismatched type for flag '%s'. Expected '%s' but actual is '%s'", name, expectedTypeName, valueTypeName) } ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/toml_command_test.go ================================================ // Disabling building of toml support in cases where golang is 1.0 or 1.1 // as the encoding library is not implemented or supported. // +build go1.2 package altsrc import ( "flag" "io/ioutil" "os" "testing" "gopkg.in/urfave/cli.v1" ) func TestCommandTomFileTest(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml"} 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("test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") os.Setenv("THE_TEST", "10") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.toml"} 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("test") expect(t, val, 10) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) defer os.Remove("current.toml") os.Setenv("THE_TEST", "10") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.toml"} 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, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml", "--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("test") expect(t, val, 7) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte(`[top] test = 15`), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml", "--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, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml"} 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("test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", Value: 7}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml"} 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, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") os.Setenv("THE_TEST", "11") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.toml"} 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("test") expect(t, val, 11) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) defer os.Remove("current.toml") os.Setenv("THE_TEST", "11") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.toml"} 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, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/toml_file_loader.go ================================================ // Disabling building of toml support in cases where golang is 1.0 or 1.1 // as the encoding library is not implemented or supported. // +build go1.2 package altsrc import ( "fmt" "reflect" "github.com/BurntSushi/toml" "gopkg.in/urfave/cli.v1" ) type tomlMap struct { Map map[interface{}]interface{} } func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) { ret = make(map[interface{}]interface{}) m := i.(map[string]interface{}) for key, val := range m { v := reflect.ValueOf(val) switch v.Kind() { case reflect.Bool: ret[key] = val.(bool) case reflect.String: ret[key] = val.(string) case reflect.Int: ret[key] = int(val.(int)) case reflect.Int8: ret[key] = int(val.(int8)) case reflect.Int16: ret[key] = int(val.(int16)) case reflect.Int32: ret[key] = int(val.(int32)) case reflect.Int64: ret[key] = int(val.(int64)) case reflect.Uint: ret[key] = int(val.(uint)) case reflect.Uint8: ret[key] = int(val.(uint8)) case reflect.Uint16: ret[key] = int(val.(uint16)) case reflect.Uint32: ret[key] = int(val.(uint32)) case reflect.Uint64: ret[key] = int(val.(uint64)) case reflect.Float32: ret[key] = float64(val.(float32)) case reflect.Float64: ret[key] = float64(val.(float64)) case reflect.Map: if tmp, err := unmarshalMap(val); err == nil { ret[key] = tmp } else { return nil, err } case reflect.Array, reflect.Slice: ret[key] = val.([]interface{}) default: return nil, fmt.Errorf("Unsupported: type = %#v", v.Kind()) } } return ret, nil } func (self *tomlMap) UnmarshalTOML(i interface{}) error { if tmp, err := unmarshalMap(i); err == nil { self.Map = tmp } else { return err } return nil } type tomlSourceContext struct { FilePath string } // NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath. func NewTomlSourceFromFile(file string) (InputSourceContext, error) { tsc := &tomlSourceContext{FilePath: file} var results tomlMap = tomlMap{} if err := readCommandToml(tsc.FilePath, &results); err != nil { return nil, fmt.Errorf("Unable to load TOML file '%s': inner error: \n'%v'", tsc.FilePath, err.Error()) } return &MapInputSource{valueMap: results.Map}, nil } // NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a provided flag name and source context. func NewTomlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { return func(context *cli.Context) (InputSourceContext, error) { filePath := context.String(flagFileName) return NewTomlSourceFromFile(filePath) } } func readCommandToml(filePath string, container interface{}) (err error) { b, err := loadDataFrom(filePath) if err != nil { return err } err = toml.Unmarshal(b, container) if err != nil { return err } err = nil return } ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/yaml_command_test.go ================================================ // Disabling building of yaml support in cases where golang is 1.0 or 1.1 // as the encoding library is not implemented or supported. // +build go1.2 package altsrc import ( "flag" "io/ioutil" "os" "testing" "gopkg.in/urfave/cli.v1" ) func TestCommandYamlFileTest(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte("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("test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte("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("test") expect(t, val, 10) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) 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) ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") test := []string{"test-cmd", "--load", "current.yaml", "--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("test") expect(t, val, 7) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) 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) ioutil.WriteFile("current.yaml", []byte("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("test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", Value: 7}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) 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) ioutil.WriteFile("current.yaml", []byte("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("test") expect(t, val, 11) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "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) } 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) } ================================================ FILE: vendor/github.com/codegangsta/cli/altsrc/yaml_file_loader.go ================================================ // Disabling building of yaml support in cases where golang is 1.0 or 1.1 // as the encoding library is not implemented or supported. // +build go1.2 package altsrc import ( "fmt" "io/ioutil" "net/http" "net/url" "os" "runtime" "strings" "gopkg.in/urfave/cli.v1" "gopkg.in/yaml.v2" ) type yamlSourceContext struct { FilePath string } // NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath. func NewYamlSourceFromFile(file string) (InputSourceContext, error) { ysc := &yamlSourceContext{FilePath: file} 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()) } return &MapInputSource{valueMap: results}, nil } // NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a provided flag name and source context. func NewYamlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { return func(context *cli.Context) (InputSourceContext, error) { filePath := context.String(flagFileName) return NewYamlSourceFromFile(filePath) } } func readCommandYaml(filePath string, container interface{}) (err error) { b, err := loadDataFrom(filePath) if err != nil { return err } err = yaml.Unmarshal(b, container) if err != nil { return err } err = nil return } func loadDataFrom(filePath string) ([]byte, error) { u, err := url.Parse(filePath) if err != nil { return nil, err } if u.Host != "" { // i have a host, now do i support the scheme? switch u.Scheme { case "http", "https": res, err := http.Get(filePath) if err != nil { return nil, err } return ioutil.ReadAll(res.Body) default: return nil, fmt.Errorf("scheme of %s is unsupported", filePath) } } else if u.Path != "" { // i dont have a host, but I have a path. I am a local file. if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil { return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) } return ioutil.ReadFile(filePath) } else if runtime.GOOS == "windows" && strings.Contains(u.String(), "\\") { // on Windows systems u.Path is always empty, so we need to check the string directly. if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil { return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) } return ioutil.ReadFile(filePath) } else { return nil, fmt.Errorf("unable to determine how to load from path %s", filePath) } } ================================================ FILE: vendor/github.com/codegangsta/cli/app.go ================================================ package cli import ( "fmt" "io" "io/ioutil" "os" "path/filepath" "sort" "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." errInvalidActionType = NewExitError("ERROR invalid Action type. "+ fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %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 { // The name of the program. Defaults to path.Base(os.Args[0]) Name string // Full name of command for help, defaults to Name HelpName string // Description of the program. Usage string // Text to override the USAGE section of help UsageText string // Description of the program argument format. ArgsUsage string // Version of the program Version string // Description of the program Description string // List of commands to execute Commands []Command // List of flags to parse Flags []Flag // Boolean to enable bash completion commands EnableBashCompletion bool // Boolean to hide built-in help command HideHelp bool // Boolean to hide built-in version flag and the VERSION section of help HideVersion bool // Populate on app startup, only gettable through method Categories() categories CommandCategories // An action to execute when the bash-completion flag is set 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 BeforeFunc // An action to execute after any subcommands are run, but after the subcommand has finished // It is run even if Action() panics After AfterFunc // The action to execute when no subcommands are specified // Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}` // *Note*: support for the deprecated `Action` signature will be removed in a future version Action interface{} // Execute this function if the proper command cannot be found CommandNotFound CommandNotFoundFunc // Execute this function if an usage error occurs OnUsageError OnUsageErrorFunc // Compilation date Compiled time.Time // List of all authors who contributed Authors []Author // Copyright of the binary if any Copyright string // Name of Author (Note: Use App.Authors, this is deprecated) Author string // Email of Author (Note: Use App.Authors, this is deprecated) 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{} // Carries a function which returns app specific info. ExtraInfo func() map[string]string // CustomAppHelpTemplate the text template for app help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomAppHelpTemplate string didSetup bool } // Tries to find out when this binary was compiled. // Returns the current time if it fails to find it. func compileTime() time.Time { info, err := os.Stat(os.Args[0]) if err != nil { return time.Now() } return info.ModTime() } // 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]), HelpName: filepath.Base(os.Args[0]), Usage: "A new cli application", UsageText: "", Version: "0.0.0", BashComplete: DefaultAppComplete, Action: helpCommand.Action, Compiled: compileTime(), Writer: os.Stdout, } } // 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}) } newCmds := []Command{} for _, c := range a.Commands { if c.HelpName == "" { c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) } newCmds = append(newCmds, c) } a.Commands = newCmds if a.Command(helpCommand.Name) == nil && !a.HideHelp { a.Commands = append(a.Commands, helpCommand) if (HelpFlag != BoolFlag{}) { a.appendFlag(HelpFlag) } } if !a.HideVersion { a.appendFlag(VersionFlag) } a.categories = CommandCategories{} for _, command := range a.Commands { a.categories = a.categories.AddCommand(command.Category, command) } sort.Sort(a.categories) if a.Metadata == nil { a.Metadata = make(map[string]interface{}) } if a.Writer == nil { a.Writer = os.Stdout } } // 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() // handle the completion flag separately from the flagset since // completion could be attempted after a flag, but before its value was put // on the command line. this causes the flagset to interpret the completion // flag name as the value of the flag before it which is undesirable // note that we can only do this because the shell autocomplete function // always appends the completion flag at the end of the command shellComplete, arguments := checkShellCompleteFlag(a, arguments) // parse flags set, err := flagSet(a.Name, a.Flags) if err != nil { return err } set.SetOutput(ioutil.Discard) err = set.Parse(arguments[1:]) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, nil) if nerr != nil { fmt.Fprintln(a.Writer, nerr) ShowAppHelp(context) return nerr } context.shellComplete = shellComplete if checkCompletions(context) { return nil } if err != nil { if a.OnUsageError != nil { err := a.OnUsageError(context, err, false) HandleExitCoder(err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) ShowAppHelp(context) return err } if !a.HideHelp && checkHelp(context) { ShowAppHelp(context) return nil } if !a.HideVersion && checkVersion(context) { ShowVersion(context) return nil } if a.After != nil { defer func() { if afterErr := a.After(context); afterErr != nil { if err != nil { err = NewMultiError(err, afterErr) } else { err = afterErr } } }() } if a.Before != nil { beforeErr := a.Before(context) if beforeErr != nil { ShowAppHelp(context) HandleExitCoder(beforeErr) err = beforeErr return err } } args := context.Args() if args.Present() { name := args.First() c := a.Command(name) if c != nil { return c.Run(context) } } if a.Action == nil { a.Action = helpCommand.Action } // Run default Action err = HandleAction(a.Action, context) HandleExitCoder(err) return err } // RunAndExitOnError calls .Run() and exits non-zero if an error was returned // // Deprecated: instead you should return an error that fulfills cli.ExitCoder // to cli.App.Run. This will cause the application to exit with the given eror // code in the cli.ExitCoder func (a *App) RunAndExitOnError() { if err := a.Run(os.Args); err != nil { fmt.Fprintln(a.errWriter(), err) OsExiter(1) } } // 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 { if a.Command(helpCommand.Name) == nil && !a.HideHelp { a.Commands = append(a.Commands, helpCommand) if (HelpFlag != BoolFlag{}) { a.appendFlag(HelpFlag) } } } newCmds := []Command{} for _, c := range a.Commands { if c.HelpName == "" { c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) } newCmds = append(newCmds, c) } a.Commands = newCmds // parse flags set, err := flagSet(a.Name, a.Flags) if err != nil { return err } set.SetOutput(ioutil.Discard) err = set.Parse(ctx.Args().Tail()) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, ctx) if nerr != nil { fmt.Fprintln(a.Writer, nerr) fmt.Fprintln(a.Writer) if len(a.Commands) > 0 { ShowSubcommandHelp(context) } else { ShowCommandHelp(ctx, context.Args().First()) } return nerr } if checkCompletions(context) { return nil } if err != nil { if a.OnUsageError != nil { err = a.OnUsageError(context, err, true) HandleExitCoder(err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) ShowSubcommandHelp(context) return err } if len(a.Commands) > 0 { if checkSubcommandHelp(context) { return nil } } else { if checkCommandHelp(ctx, context.Args().First()) { return nil } } if a.After != nil { defer func() { afterErr := a.After(context) if afterErr != nil { HandleExitCoder(err) if err != nil { err = NewMultiError(err, afterErr) } else { err = afterErr } } }() } if a.Before != nil { beforeErr := a.Before(context) if beforeErr != nil { HandleExitCoder(beforeErr) err = beforeErr return err } } args := context.Args() if args.Present() { name := args.First() c := a.Command(name) if c != nil { return c.Run(context) } } // Run default Action err = HandleAction(a.Action, context) HandleExitCoder(err) return err } // 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) { return &c } } return nil } // 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 { return true } } 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) } } // Author represents someone who has contributed to a cli project. type Author struct { Name string // The Authors name Email string // The Authors email } // String makes Author comply to the Stringer interface, to allow an easy print in the templating process func (a Author) String() string { e := "" if a.Email != "" { e = " <" + a.Email + ">" } return fmt.Sprintf("%v%v", a.Name, e) } // HandleAction attempts to figure out which Action signature was used. 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) { if a, ok := action.(ActionFunc); ok { return a(context) } else if a, ok := action.(func(*Context) error); ok { return a(context) } else if a, ok := action.(func(*Context)); ok { // deprecated function signature a(context) return nil } else { return errInvalidActionType } } ================================================ FILE: vendor/github.com/codegangsta/cli/app_test.go ================================================ package cli import ( "bytes" "errors" "flag" "fmt" "io" "io/ioutil" "os" "reflect" "strings" "testing" ) var ( lastExitCode = 0 fakeOsExiter = func(rc int) { lastExitCode = rc } fakeErrWriter = &bytes.Buffer{} ) func init() { OsExiter = fakeOsExiter ErrWriter = fakeErrWriter } 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"} app := NewApp() app.Name = "greet" app.Flags = []Flag{ StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, } 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{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}} app.Run(os.Args) // Output: // Hello Jeremy } func ExampleApp_Run_subcommand() { // set args for examples sake os.Args = []string{"say", "hi", "english", "--name", "Jeremy"} app := NewApp() app.Name = "say" app.Commands = []Command{ { Name: "hello", Aliases: []string{"hi"}, Usage: "use it to see a description", Description: "This is how we describe hello the function", Subcommands: []Command{ { Name: "english", Aliases: []string{"en"}, Usage: "sends a greeting in english", Description: "greets someone in english", Flags: []Flag{ StringFlag{ Name: "name", Value: "Bob", Usage: "Name of the person to greet", }, }, Action: func(c *Context) error { fmt.Println("Hello,", c.String("name")) return nil }, }, }, }, } app.Run(os.Args) // Output: // Hello, Jeremy } func ExampleApp_Run_appHelp() { // set args for examples sake os.Args = []string{"greet", "help"} app := NewApp() app.Name = "greet" app.Version = "0.1.0" app.Description = "This is how we describe greet the app" app.Authors = []Author{ {Name: "Harrison", Email: "harrison@lolwut.com"}, {Name: "Oliver Allen", Email: "oliver@toyshop.com"}, } app.Flags = []Flag{ StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, } app.Commands = []Command{ { Name: "describeit", Aliases: []string{"d"}, Usage: "use it to see a description", Description: "This is how we describe describeit the function", Action: func(c *Context) error { fmt.Printf("i like to describe things") return nil }, }, } app.Run(os.Args) // Output: // NAME: // greet - A new cli application // // USAGE: // greet [global options] command [command options] [arguments...] // // VERSION: // 0.1.0 // // DESCRIPTION: // This is how we describe greet the app // // AUTHORS: // Harrison // Oliver Allen // // COMMANDS: // describeit, d use it to see a description // help, h Shows a list of commands or help for one command // // GLOBAL OPTIONS: // --name value a name to say (default: "bob") // --help, -h show help // --version, -v print the version } func ExampleApp_Run_commandHelp() { // set args for examples sake os.Args = []string{"greet", "h", "describeit"} app := NewApp() app.Name = "greet" app.Flags = []Flag{ StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, } app.Commands = []Command{ { Name: "describeit", Aliases: []string{"d"}, Usage: "use it to see a description", Description: "This is how we describe describeit the function", Action: func(c *Context) error { fmt.Printf("i like to describe things") return nil }, }, } app.Run(os.Args) // Output: // NAME: // greet describeit - use it to see a description // // USAGE: // greet describeit [arguments...] // // DESCRIPTION: // This is how we describe describeit the function } func ExampleApp_Run_noAction() { app := App{} app.Name = "greet" app.Run([]string{"greet"}) // Output: // NAME: // greet // // USAGE: // [global options] command [command options] [arguments...] // // COMMANDS: // help, h Shows a list of commands or help for one command // // GLOBAL OPTIONS: // --help, -h show help // --version, -v print the version } func ExampleApp_Run_subcommandNoAction() { app := App{} app.Name = "greet" app.Commands = []Command{ { Name: "describeit", Aliases: []string{"d"}, Usage: "use it to see a description", Description: "This is how we describe describeit the function", }, } app.Run([]string{"greet", "describeit"}) // Output: // NAME: // describeit - use it to see a description // // USAGE: // describeit [arguments...] // // DESCRIPTION: // This is how we describe describeit the function } func ExampleApp_Run_bashComplete() { // set args for examples sake os.Args = []string{"greet", "--generate-bash-completion"} app := NewApp() app.Name = "greet" app.EnableBashCompletion = true app.Commands = []Command{ { Name: "describeit", Aliases: []string{"d"}, Usage: "use it to see a description", Description: "This is how we describe describeit the function", 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) error { fmt.Printf("the next example") return nil }, }, } app.Run(os.Args) // Output: // describeit // d // next // help // h } func TestApp_Run(t *testing.T) { s := "" app := NewApp() app.Action = func(c *Context) error { s = s + c.Args().First() return nil } err := app.Run([]string{"command", "foo"}) expect(t, err, nil) err = app.Run([]string{"command", "bar"}) expect(t, err, nil) expect(t, s, "foobar") } var commandAppTests = []struct { name string expected bool }{ {"foobar", true}, {"batbaz", true}, {"b", true}, {"f", true}, {"bat", false}, {"nothing", false}, } func TestApp_Command(t *testing.T) { app := NewApp() fooCommand := Command{Name: "foobar", Aliases: []string{"f"}} batCommand := Command{Name: "batbaz", Aliases: []string{"b"}} app.Commands = []Command{ fooCommand, batCommand, } for _, test := range commandAppTests { expect(t, app.Command(test.name) != nil, test.expected) } } func TestApp_Setup_defaultsWriter(t *testing.T) { app := &App{} app.Setup() expect(t, app.Writer, os.Stdout) } func TestApp_CommandWithArgBeforeFlags(t *testing.T) { var parsedOption, firstArg string app := NewApp() command := Command{ Name: "cmd", Flags: []Flag{ StringFlag{Name: "option", Value: "", Usage: "some option"}, }, Action: func(c *Context) error { parsedOption = c.String("option") firstArg = c.Args().First() return nil }, } app.Commands = []Command{command} app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"}) expect(t, parsedOption, "my-option") expect(t, firstArg, "my-arg") } func TestApp_RunAsSubcommandParseFlags(t *testing.T) { var context *Context a := NewApp() a.Commands = []Command{ { Name: "foo", Action: func(c *Context) error { context = c return nil }, Flags: []Flag{ StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", }, }, Before: func(_ *Context) error { return nil }, }, } a.Run([]string{"", "foo", "--lang", "spanish", "abcd"}) expect(t, context.Args().Get(0), "abcd") expect(t, context.String("lang"), "spanish") } func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) { a := App{ Flags: []Flag{ StringFlag{Name: "--foo"}, }, Writer: bytes.NewBufferString(""), } set := flag.NewFlagSet("", flag.ContinueOnError) set.Parse([]string{"", "---foo"}) c := &Context{flagSet: set} err := a.RunAsSubcommand(c) expect(t, err, errors.New("bad flag syntax: ---foo")) } func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) { var parsedOption string var args []string app := NewApp() command := Command{ Name: "cmd", Flags: []Flag{ StringFlag{Name: "option", Value: "", Usage: "some option"}, }, Action: func(c *Context) error { parsedOption = c.String("option") args = c.Args() return nil }, } app.Commands = []Command{command} app.Run([]string{"", "cmd", "my-arg", "--option", "my-option", "--", "--notARealFlag"}) expect(t, parsedOption, "my-option") expect(t, args[0], "my-arg") expect(t, args[1], "--") expect(t, args[2], "--notARealFlag") } func TestApp_CommandWithDash(t *testing.T) { var args []string app := NewApp() command := Command{ Name: "cmd", Action: func(c *Context) error { args = c.Args() return nil }, } app.Commands = []Command{command} app.Run([]string{"", "cmd", "my-arg", "-"}) expect(t, args[0], "my-arg") expect(t, args[1], "-") } func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) { var args []string app := NewApp() command := Command{ Name: "cmd", Action: func(c *Context) error { args = c.Args() return nil }, } app.Commands = []Command{command} app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"}) expect(t, args[0], "my-arg") expect(t, args[1], "--") 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 app := NewApp() app.Flags = []Flag{ Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, } app.Action = func(c *Context) error { meters = c.Float64("height") return nil } app.Run([]string{"", "--height", "1.93"}) expect(t, meters, 1.93) } func TestApp_ParseSliceFlags(t *testing.T) { var parsedOption, firstArg string var parsedIntSlice []int var parsedStringSlice []string app := NewApp() command := Command{ Name: "cmd", Flags: []Flag{ 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) error { parsedIntSlice = c.IntSlice("p") parsedStringSlice = c.StringSlice("ip") parsedOption = c.String("option") firstArg = c.Args().First() return nil }, } app.Commands = []Command{command} app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"}) IntsEquals := func(a, b []int) bool { if len(a) != len(b) { return false } for i, v := range a { if v != b[i] { return false } } return true } StrsEquals := func(a, b []string) bool { if len(a) != len(b) { return false } for i, v := range a { if v != b[i] { return false } } return true } var expectedIntSlice = []int{22, 80} var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"} if !IntsEquals(parsedIntSlice, expectedIntSlice) { t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice) } if !StrsEquals(parsedStringSlice, expectedStringSlice) { t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice) } } func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) { var parsedIntSlice []int var parsedStringSlice []string app := NewApp() command := Command{ Name: "cmd", Flags: []Flag{ IntSliceFlag{Name: "a", Usage: "set numbers"}, StringSliceFlag{Name: "str", Usage: "set strings"}, }, Action: func(c *Context) error { parsedIntSlice = c.IntSlice("a") parsedStringSlice = c.StringSlice("str") return nil }, } app.Commands = []Command{command} app.Run([]string{"", "cmd", "my-arg", "-a", "2", "-str", "A"}) var expectedIntSlice = []int{2} var expectedStringSlice = []string{"A"} if parsedIntSlice[0] != expectedIntSlice[0] { t.Errorf("%v does not match %v", parsedIntSlice[0], expectedIntSlice[0]) } if parsedStringSlice[0] != expectedStringSlice[0] { t.Errorf("%v does not match %v", parsedIntSlice[0], expectedIntSlice[0]) } } func TestApp_DefaultStdout(t *testing.T) { app := NewApp() if app.Writer != os.Stdout { t.Error("Default output writer not set.") } } type mockWriter struct { written []byte } func (fw *mockWriter) Write(p []byte) (n int, err error) { if fw.written == nil { fw.written = p } else { fw.written = append(fw.written, p...) } return len(p), nil } func (fw *mockWriter) GetWritten() (b []byte) { return fw.written } func TestApp_SetStdout(t *testing.T) { w := &mockWriter{} app := NewApp() app.Name = "test" app.Writer = w err := app.Run([]string{"help"}) if err != nil { t.Fatalf("Run error: %s", err) } if len(w.written) == 0 { t.Error("App did not write output to desired writer.") } } func TestApp_BeforeFunc(t *testing.T) { counts := &opCounts{} beforeError := fmt.Errorf("fail") var err error app := NewApp() app.Before = func(c *Context) error { counts.Total++ counts.Before = counts.Total s := c.String("opt") if s == "fail" { return beforeError } return nil } app.Commands = []Command{ { Name: "sub", Action: func(c *Context) error { counts.Total++ counts.SubCommand = counts.Total return nil }, }, } app.Flags = []Flag{ StringFlag{Name: "opt"}, } // run with the Before() func succeeding err = app.Run([]string{"command", "--opt", "succeed", "sub"}) if err != nil { t.Fatalf("Run error: %s", err) } if counts.Before != 1 { t.Errorf("Before() not executed when expected") } if counts.SubCommand != 2 { t.Errorf("Subcommand not executed when expected") } // reset counts = &opCounts{} // run with the Before() func failing err = app.Run([]string{"command", "--opt", "fail", "sub"}) // should be the same error produced by the Before func if err != beforeError { t.Errorf("Run error 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") } // 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) { counts := &opCounts{} afterError := fmt.Errorf("fail") var err error app := NewApp() app.After = func(c *Context) error { counts.Total++ counts.After = counts.Total s := c.String("opt") if s == "fail" { return afterError } return nil } app.Commands = []Command{ { Name: "sub", Action: func(c *Context) error { counts.Total++ counts.SubCommand = counts.Total return nil }, }, } app.Flags = []Flag{ StringFlag{Name: "opt"}, } // run with the After() func succeeding err = app.Run([]string{"command", "--opt", "succeed", "sub"}) if err != nil { t.Fatalf("Run error: %s", err) } if counts.After != 2 { t.Errorf("After() not executed when expected") } if counts.SubCommand != 1 { t.Errorf("Subcommand not executed when expected") } // reset counts = &opCounts{} // run with the Before() func failing err = app.Run([]string{"command", "--opt", "fail", "sub"}) // should be the same error produced by the Before func if err != afterError { t.Errorf("Run error expected, but not received") } if counts.After != 2 { t.Errorf("After() not executed when expected") } if counts.SubCommand != 1 { t.Errorf("Subcommand not executed when expected") } } func TestAppNoHelpFlag(t *testing.T) { oldFlag := HelpFlag defer func() { HelpFlag = oldFlag }() HelpFlag = BoolFlag{} app := NewApp() app.Writer = ioutil.Discard err := app.Run([]string{"test", "-h"}) if err != flag.ErrHelp { t.Errorf("expected error about missing help flag, but got: %s (%T)", err, err) } } func TestAppHelpPrinter(t *testing.T) { oldPrinter := HelpPrinter defer func() { HelpPrinter = oldPrinter }() var wasCalled = false HelpPrinter = func(w io.Writer, template string, data interface{}) { wasCalled = true } app := NewApp() app.Run([]string{"-h"}) if wasCalled == false { t.Errorf("Help printer expected to be called, but was not") } } func TestApp_VersionPrinter(t *testing.T) { oldPrinter := VersionPrinter defer func() { VersionPrinter = oldPrinter }() var wasCalled = false VersionPrinter = func(c *Context) { wasCalled = true } app := NewApp() ctx := NewContext(app, nil, nil) ShowVersion(ctx) if wasCalled == false { t.Errorf("Version printer expected to be called, but was not") } } func TestApp_CommandNotFound(t *testing.T) { counts := &opCounts{} app := NewApp() app.CommandNotFound = func(c *Context, command string) { counts.Total++ counts.CommandNotFound = counts.Total } app.Commands = []Command{ { Name: "bar", Action: func(c *Context) error { counts.Total++ counts.SubCommand = counts.Total return nil }, }, } app.Run([]string{"command", "foo"}) expect(t, counts.CommandNotFound, 1) expect(t, counts.SubCommand, 0) expect(t, counts.Total, 1) } func TestApp_OrderOfOperations(t *testing.T) { counts := &opCounts{} resetCounts := func() { counts = &opCounts{} } app := NewApp() 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{ { Name: "bar", Action: func(c *Context) error { counts.Total++ counts.SubCommand = counts.Total return nil }, }, } app.Action = func(c *Context) error { counts.Total++ counts.Action = counts.Total return nil } _ = 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) { var subcommandHelpTopics = [][]string{ {"command", "foo", "--help"}, {"command", "foo", "-h"}, {"command", "foo", "help"}, } for _, flagSet := range subcommandHelpTopics { t.Logf("==> checking with flags %v", flagSet) app := NewApp() buf := new(bytes.Buffer) app.Writer = buf subCmdBar := Command{ Name: "bar", Usage: "does bar things", } subCmdBaz := Command{ Name: "baz", Usage: "does baz things", } cmd := Command{ Name: "foo", Description: "descriptive wall of text about how it does foo things", Subcommands: []Command{subCmdBar, subCmdBaz}, Action: func(c *Context) error { return nil }, } app.Commands = []Command{cmd} err := app.Run(flagSet) if err != nil { t.Error(err) } output := buf.String() t.Logf("output: %q\n", buf.Bytes()) if strings.Contains(output, "No help topic for") { t.Errorf("expect a help topic, got none: \n%q", output) } for _, shouldContain := range []string{ cmd.Name, cmd.Description, subCmdBar.Name, subCmdBar.Usage, subCmdBaz.Name, subCmdBaz.Usage, } { if !strings.Contains(output, shouldContain) { t.Errorf("want help to contain %q, did not: \n%q", shouldContain, output) } } } } func TestApp_Run_SubcommandFullPath(t *testing.T) { app := NewApp() buf := new(bytes.Buffer) app.Writer = buf app.Name = "command" subCmd := Command{ Name: "bar", Usage: "does bar things", } cmd := Command{ Name: "foo", Description: "foo commands", Subcommands: []Command{subCmd}, } app.Commands = []Command{cmd} err := app.Run([]string{"command", "foo", "bar", "--help"}) if err != nil { t.Error(err) } output := buf.String() if !strings.Contains(output, "command foo bar - does bar things") { t.Errorf("expected full path to subcommand: %s", output) } if !strings.Contains(output, "command foo bar [arguments...]") { t.Errorf("expected full path to subcommand: %s", output) } } func TestApp_Run_SubcommandHelpName(t *testing.T) { app := NewApp() buf := new(bytes.Buffer) app.Writer = buf app.Name = "command" subCmd := Command{ Name: "bar", HelpName: "custom", Usage: "does bar things", } cmd := Command{ Name: "foo", Description: "foo commands", Subcommands: []Command{subCmd}, } app.Commands = []Command{cmd} err := app.Run([]string{"command", "foo", "bar", "--help"}) if err != nil { t.Error(err) } output := buf.String() if !strings.Contains(output, "custom - does bar things") { t.Errorf("expected HelpName for subcommand: %s", output) } if !strings.Contains(output, "custom [arguments...]") { t.Errorf("expected HelpName to subcommand: %s", output) } } func TestApp_Run_CommandHelpName(t *testing.T) { app := NewApp() buf := new(bytes.Buffer) app.Writer = buf app.Name = "command" subCmd := Command{ Name: "bar", Usage: "does bar things", } cmd := Command{ Name: "foo", HelpName: "custom", Description: "foo commands", Subcommands: []Command{subCmd}, } app.Commands = []Command{cmd} err := app.Run([]string{"command", "foo", "bar", "--help"}) if err != nil { t.Error(err) } output := buf.String() if !strings.Contains(output, "command foo bar - does bar things") { t.Errorf("expected full path to subcommand: %s", output) } if !strings.Contains(output, "command foo bar [arguments...]") { t.Errorf("expected full path to subcommand: %s", output) } } func TestApp_Run_CommandSubcommandHelpName(t *testing.T) { app := NewApp() buf := new(bytes.Buffer) app.Writer = buf app.Name = "base" subCmd := Command{ Name: "bar", HelpName: "custom", Usage: "does bar things", } cmd := Command{ Name: "foo", Description: "foo commands", Subcommands: []Command{subCmd}, } app.Commands = []Command{cmd} err := app.Run([]string{"command", "foo", "--help"}) if err != nil { t.Error(err) } output := buf.String() if !strings.Contains(output, "base foo - foo commands") { t.Errorf("expected full path to subcommand: %s", output) } if !strings.Contains(output, "base foo command [command options] [arguments...]") { t.Errorf("expected full path to subcommand: %s", output) } } func TestApp_Run_Help(t *testing.T) { var helpArguments = [][]string{{"boom", "--help"}, {"boom", "-h"}, {"boom", "help"}} for _, args := range helpArguments { buf := new(bytes.Buffer) t.Logf("==> checking with arguments %v", args) app := NewApp() app.Name = "boom" app.Usage = "make an explosive entrance" app.Writer = buf app.Action = func(c *Context) error { buf.WriteString("boom I say!") return nil } err := app.Run(args) if err != nil { t.Error(err) } output := buf.String() t.Logf("output: %q\n", buf.Bytes()) if !strings.Contains(output, "boom - make an explosive entrance") { t.Errorf("want help to contain %q, did not: \n%q", "boom - make an explosive entrance", output) } } } func TestApp_Run_Version(t *testing.T) { var versionArguments = [][]string{{"boom", "--version"}, {"boom", "-v"}} for _, args := range versionArguments { buf := new(bytes.Buffer) t.Logf("==> checking with arguments %v", args) app := NewApp() app.Name = "boom" app.Usage = "make an explosive entrance" app.Version = "0.1.0" app.Writer = buf app.Action = func(c *Context) error { buf.WriteString("boom I say!") return nil } err := app.Run(args) if err != nil { t.Error(err) } output := buf.String() t.Logf("output: %q\n", buf.Bytes()) if !strings.Contains(output, "0.1.0") { t.Errorf("want version to contain %q, did not: \n%q", "0.1.0", output) } } } func TestApp_Run_Categories(t *testing.T) { app := NewApp() app.Name = "categories" app.HideHelp = true app.Commands = []Command{ { Name: "command1", Category: "1", }, { Name: "command2", Category: "1", }, { Name: "command3", Category: "2", }, } buf := new(bytes.Buffer) app.Writer = buf app.Run([]string{"categories"}) expect := CommandCategories{ &CommandCategory{ Name: "1", Commands: []Command{ app.Commands[0], app.Commands[1], }, }, &CommandCategory{ Name: "2", Commands: []Command{ app.Commands[2], }, }, } if !reflect.DeepEqual(app.Categories(), expect) { t.Fatalf("expected categories %#v, to equal %#v", app.Categories(), expect) } 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) } } 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) 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") } err := app.Run([]string{"foo"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.Contains(err.Error(), "before error") { t.Errorf("expected text of error from Before method, but got none in \"%v\"", err) } if !strings.Contains(err.Error(), "after error") { t.Errorf("expected text of error from After method, but got none in \"%v\"", err) } } func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) { app := NewApp() app.Commands = []Command{ { Subcommands: []Command{ { Name: "sub", }, }, Name: "bar", Before: func(c *Context) error { return fmt.Errorf("before error") }, After: func(c *Context) error { return fmt.Errorf("after error") }, }, } err := app.Run([]string{"foo", "bar"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.Contains(err.Error(), "before error") { t.Errorf("expected text of error from Before method, but got none in \"%v\"", err) } if !strings.Contains(err.Error(), "after error") { t.Errorf("expected text of error from After method, but got none in \"%v\"", err) } } func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) { app := NewApp() app.Flags = []Flag{ IntFlag{Name: "flag"}, } app.OnUsageError = func(c *Context, err error, isSubcommand bool) error { if isSubcommand { t.Errorf("Expect no subcommand") } if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { t.Errorf("Expect an invalid value error, but got \"%v\"", err) } return errors.New("intercepted: " + err.Error()) } app.Commands = []Command{ { Name: "bar", }, } err := app.Run([]string{"foo", "--flag=wrong"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { t.Errorf("Expect an intercepted error, but got \"%v\"", err) } } func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) { app := NewApp() app.Flags = []Flag{ IntFlag{Name: "flag"}, } app.OnUsageError = func(c *Context, err error, isSubcommand bool) error { if isSubcommand { t.Errorf("Expect subcommand") } if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { t.Errorf("Expect an invalid value error, but got \"%v\"", err) } return errors.New("intercepted: " + err.Error()) } app.Commands = []Command{ { Name: "bar", }, } err := app.Run([]string{"foo", "--flag=wrong", "bar"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { t.Errorf("Expect an intercepted error, but got \"%v\"", err) } } // A custom flag that conforms to the relevant interfaces, but has none of the // fields that the other flag types do. type customBoolFlag struct { Nombre string } // Don't use the normal FlagStringer func (c *customBoolFlag) String() string { return "***" + c.Nombre + "***" } func (c *customBoolFlag) GetName() string { return c.Nombre } func (c *customBoolFlag) Apply(set *flag.FlagSet) { set.String(c.Nombre, c.Nombre, "") } func TestCustomFlagsUnused(t *testing.T) { app := NewApp() app.Flags = []Flag{&customBoolFlag{"custom"}} err := app.Run([]string{"foo"}) if err != nil { t.Errorf("Run returned unexpected error: %v", err) } } func TestCustomFlagsUsed(t *testing.T) { app := NewApp() app.Flags = []Flag{&customBoolFlag{"custom"}} err := app.Run([]string{"foo", "--custom=bar"}) if err != nil { t.Errorf("Run returned unexpected error: %v", err) } } func TestCustomHelpVersionFlags(t *testing.T) { app := NewApp() // Be sure to reset the global flags defer func(helpFlag Flag, versionFlag Flag) { HelpFlag = helpFlag VersionFlag = versionFlag }(HelpFlag, VersionFlag) HelpFlag = &customBoolFlag{"help-custom"} VersionFlag = &customBoolFlag{"version-custom"} err := app.Run([]string{"foo", "--help-custom=bar"}) if err != nil { t.Errorf("Run returned unexpected error: %v", err) } } func TestHandleAction_WithNonFuncAction(t *testing.T) { app := NewApp() app.Action = 42 fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } err = HandleAction(app.Action, NewContext(app, fs, 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 "" } fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } err = HandleAction(app.Action, NewContext(app, fs, 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_WithInvalidFuncReturnSignature(t *testing.T) { app := NewApp() app.Action = func(_ *Context) (int, error) { return 0, nil } fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } err = HandleAction(app.Action, NewContext(app, fs, 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 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 } fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } HandleAction(app.Action, NewContext(app, fs, nil)) } func TestShellCompletionForIncompleteFlags(t *testing.T) { app := NewApp() app.Flags = []Flag{ IntFlag{ Name: "test-completion", }, } app.EnableBashCompletion = true app.BashComplete = func(ctx *Context) { for _, command := range ctx.App.Commands { if command.Hidden { continue } for _, name := range command.Names() { fmt.Fprintln(ctx.App.Writer, name) } } for _, flag := range ctx.App.Flags { for _, name := range strings.Split(flag.GetName(), ",") { if name == BashCompletionFlag.GetName() { continue } switch name = strings.TrimSpace(name); len(name) { case 0: case 1: fmt.Fprintln(ctx.App.Writer, "-"+name) default: fmt.Fprintln(ctx.App.Writer, "--"+name) } } } } app.Action = func(ctx *Context) error { return fmt.Errorf("should not get here") } err := app.Run([]string{"", "--test-completion", "--" + BashCompletionFlag.GetName()}) if err != nil { t.Errorf("app should not return an error: %s", err) } } func TestHandleActionActuallyWorksWithActions(t *testing.T) { var f ActionFunc called := false f = func(c *Context) error { called = true return nil } err := HandleAction(f, nil) if err != nil { t.Errorf("Should not have errored: %v", err) } if !called { t.Errorf("Function was not called") } } ================================================ FILE: vendor/github.com/codegangsta/cli/appveyor.yml ================================================ version: "{build}" os: Windows Server 2016 image: Visual Studio 2017 clone_folder: c:\gopath\src\github.com\urfave\cli environment: GOPATH: C:\gopath GOVERSION: 1.8.x PYTHON: C:\Python36-x64 PYTHON_VERSION: 3.6.x PYTHON_ARCH: 64 install: - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% - go version - go env - go get github.com/urfave/gfmrun/... - go get -v -t ./... build_script: - python runtests vet - python runtests test - python runtests gfmrun ================================================ FILE: vendor/github.com/codegangsta/cli/autocomplete/bash_autocomplete ================================================ #! /bin/bash : ${PROG:=$(basename ${BASH_SOURCE})} _cli_bash_autocomplete() { local cur opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 } complete -F _cli_bash_autocomplete $PROG unset PROG ================================================ FILE: vendor/github.com/codegangsta/cli/autocomplete/zsh_autocomplete ================================================ autoload -U compinit && compinit autoload -U bashcompinit && bashcompinit script_dir=$(dirname $0) source ${script_dir}/bash_autocomplete ================================================ FILE: vendor/github.com/codegangsta/cli/category.go ================================================ package cli // CommandCategories is a slice of *CommandCategory. type CommandCategories []*CommandCategory // CommandCategory is a category containing commands. type CommandCategory struct { Name string Commands Commands } func (c CommandCategories) Less(i, j int) bool { return c[i].Name < c[j].Name } func (c CommandCategories) Len() int { return len(c) } func (c CommandCategories) Swap(i, j int) { 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 { commandCategory.Commands = append(commandCategory.Commands, command) return c } } 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 } ================================================ FILE: vendor/github.com/codegangsta/cli/cli.go ================================================ // Package cli provides a minimal framework for creating and organizing command line // Go applications. cli is designed to be easy to understand and write, the most simple // cli application can be written as follows: // func main() { // cli.NewApp().Run(os.Args) // } // // Of course this application does not do much, so let's make this an actual application: // func main() { // app := cli.NewApp() // app.Name = "greet" // app.Usage = "say a greeting" // app.Action = func(c *cli.Context) error { // println("Greetings") // return nil // } // // app.Run(os.Args) // } package cli //go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go ================================================ FILE: vendor/github.com/codegangsta/cli/command.go ================================================ package cli import ( "fmt" "io/ioutil" "sort" "strings" ) // Command is a subcommand for a cli.App. type Command struct { // The name of the command Name string // short name of the command. Typically one character (deprecated, use `Aliases`) ShortName string // A list of aliases for the command Aliases []string // A short description of the usage of this command Usage string // Custom text to show on USAGE section of help UsageText string // A longer explanation of how the command works Description string // A short description of the arguments of this command ArgsUsage string // The category the command is part of Category string // The function to call when checking for bash command completions 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 BeforeFunc // An action to execute after any subcommands are run, but after the subcommand has finished // It is run even if Action() panics After AfterFunc // The function to call when this command is invoked 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 Flags []Flag // Treat all flags as normal arguments if true SkipFlagParsing bool // Skip argument reordering which attempts to move flags before arguments, // but only works if all flags appear after all arguments. This behavior was // removed n version 2 since it only works under specific conditions so we // backport here by exposing it as an option for compatibility. SkipArgReorder 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 // CustomHelpTemplate 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. CustomHelpTemplate string } type CommandsByName []Command func (c CommandsByName) Len() int { return len(c) } func (c CommandsByName) Less(i, j int) bool { return c[i].Name < c[j].Name } func (c CommandsByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] } // 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 { return c.Name } return strings.Join(c.commandNamePath, " ") } // Commands is a slice of Command type Commands []Command // 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) } if !c.HideHelp && (HelpFlag != BoolFlag{}) { // append help to flags c.Flags = append( c.Flags, HelpFlag, ) } set, err := flagSet(c.Name, c.Flags) if err != nil { return err } set.SetOutput(ioutil.Discard) if c.SkipFlagParsing { err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) } else if !c.SkipArgReorder { firstFlagIndex := -1 terminatorIndex := -1 for index, arg := range ctx.Args() { if arg == "--" { terminatorIndex = index break } else if arg == "-" { // Do nothing. A dash alone is not really a flag. continue } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { firstFlagIndex = index } } if firstFlagIndex > -1 { args := ctx.Args() regularArgs := make([]string, len(args[1:firstFlagIndex])) copy(regularArgs, args[1:firstFlagIndex]) var flagArgs []string if terminatorIndex > -1 { flagArgs = args[firstFlagIndex:terminatorIndex] regularArgs = append(regularArgs, args[terminatorIndex:]...) } else { flagArgs = args[firstFlagIndex:] } err = set.Parse(append(flagArgs, regularArgs...)) } else { err = set.Parse(ctx.Args().Tail()) } } else { err = set.Parse(ctx.Args().Tail()) } nerr := normalizeFlags(c.Flags, set) if nerr != nil { fmt.Fprintln(ctx.App.Writer, nerr) fmt.Fprintln(ctx.App.Writer) ShowCommandHelp(ctx, c.Name) return nerr } context := NewContext(ctx.App, set, ctx) context.Command = c if checkCommandCompletions(context, c.Name) { return nil } if err != nil { if c.OnUsageError != nil { err := c.OnUsageError(context, err, false) HandleExitCoder(err) return err } fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) fmt.Fprintln(context.App.Writer) ShowCommandHelp(context, c.Name) return err } if checkCommandHelp(context, c.Name) { return nil } if c.After != nil { defer func() { afterErr := c.After(context) if afterErr != nil { HandleExitCoder(err) if err != nil { err = NewMultiError(err, afterErr) } else { err = afterErr } } }() } if c.Before != nil { err = c.Before(context) if err != nil { ShowCommandHelp(context, c.Name) HandleExitCoder(err) return err } } if c.Action == nil { c.Action = helpSubcommand.Action } 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} if c.ShortName != "" { names = append(names, c.ShortName) } return append(names, c.Aliases...) } // 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 { return true } } return false } 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 == "" { app.HelpName = c.HelpName } else { app.HelpName = app.Name } app.Usage = c.Usage app.Description = c.Description app.ArgsUsage = c.ArgsUsage // set CommandNotFound app.CommandNotFound = ctx.App.CommandNotFound app.CustomAppHelpTemplate = c.CustomHelpTemplate // set the flags and commands app.Commands = c.Subcommands app.Flags = c.Flags app.HideHelp = c.HideHelp app.Version = ctx.App.Version app.HideVersion = ctx.App.HideVersion app.Compiled = ctx.App.Compiled app.Author = ctx.App.Author app.Email = ctx.App.Email app.Writer = ctx.App.Writer app.ErrWriter = ctx.App.ErrWriter app.categories = CommandCategories{} for _, command := range c.Subcommands { app.categories = app.categories.AddCommand(command.Category, command) } sort.Sort(app.categories) // bash completion app.EnableBashCompletion = ctx.App.EnableBashCompletion if c.BashComplete != nil { app.BashComplete = c.BashComplete } // set the actions app.Before = c.Before app.After = c.After if c.Action != nil { app.Action = c.Action } else { app.Action = helpSubcommand.Action } app.OnUsageError = c.OnUsageError for index, cc := range app.Commands { app.Commands[index].commandNamePath = []string{c.Name, cc.Name} } return app.RunAsSubcommand(ctx) } // VisibleFlags returns a slice of the Flags with Hidden=false func (c Command) VisibleFlags() []Flag { return visibleFlags(c.Flags) } ================================================ FILE: vendor/github.com/codegangsta/cli/command_test.go ================================================ package cli import ( "errors" "flag" "fmt" "io/ioutil" "strings" "testing" ) func TestCommandFlagParsing(t *testing.T) { cases := []struct { testArgs []string skipFlagParsing bool skipArgReorder bool expectedErr error }{ // Test normal "not ignoring flags" flow {[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break")}, // Test no arg reorder {[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil}, {[]string{"test-cmd", "blah", "blah"}, true, false, nil}, // Test SkipFlagParsing without any args that look like flags {[]string{"test-cmd", "blah", "-break"}, true, false, nil}, // Test SkipFlagParsing with random flag arg {[]string{"test-cmd", "blah", "-help"}, true, false, nil}, // Test SkipFlagParsing with "special" help flag arg } for _, c := range cases { app := NewApp() app.Writer = ioutil.Discard set := flag.NewFlagSet("test", 0) set.Parse(c.testArgs) context := NewContext(app, set, nil) command := Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(_ *Context) error { return nil }, SkipFlagParsing: c.skipFlagParsing, SkipArgReorder: c.skipArgReorder, } err := command.Run(context) expect(t, err, c.expectedErr) expect(t, []string(context.Args()), c.testArgs) } } func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { app := NewApp() app.Commands = []Command{ { Name: "bar", Before: func(c *Context) error { return fmt.Errorf("before error") }, After: func(c *Context) error { return fmt.Errorf("after error") }, }, } err := app.Run([]string{"foo", "bar"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.Contains(err.Error(), "before error") { t.Errorf("expected text of error from Before method, but got none in \"%v\"", err) } if !strings.Contains(err.Error(), "after error") { t.Errorf("expected text of error from After method, but got none in \"%v\"", err) } } func TestCommand_Run_BeforeSavesMetadata(t *testing.T) { var receivedMsgFromAction string var receivedMsgFromAfter string app := NewApp() app.Commands = []Command{ { Name: "bar", Before: func(c *Context) error { c.App.Metadata["msg"] = "hello world" return nil }, Action: func(c *Context) error { msg, ok := c.App.Metadata["msg"] if !ok { return errors.New("msg not found") } receivedMsgFromAction = msg.(string) return nil }, After: func(c *Context) error { msg, ok := c.App.Metadata["msg"] if !ok { return errors.New("msg not found") } receivedMsgFromAfter = msg.(string) return nil }, }, } err := app.Run([]string{"foo", "bar"}) if err != nil { t.Fatalf("expected no error from Run, got %s", err) } expectedMsg := "hello world" if receivedMsgFromAction != expectedMsg { t.Fatalf("expected msg from Action to match. Given: %q\nExpected: %q", receivedMsgFromAction, expectedMsg) } if receivedMsgFromAfter != expectedMsg { t.Fatalf("expected msg from After to match. Given: %q\nExpected: %q", receivedMsgFromAction, expectedMsg) } } func TestCommand_OnUsageError_hasCommandContext(t *testing.T) { app := NewApp() app.Commands = []Command{ { Name: "bar", Flags: []Flag{ IntFlag{Name: "flag"}, }, OnUsageError: func(c *Context, err error, _ bool) error { return fmt.Errorf("intercepted in %s: %s", c.Command.Name, err.Error()) }, }, } err := app.Run([]string{"foo", "bar", "--flag=wrong"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.HasPrefix(err.Error(), "intercepted in bar") { t.Errorf("Expect an intercepted error, but got \"%v\"", err) } } func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) { app := NewApp() app.Commands = []Command{ { Name: "bar", Flags: []Flag{ IntFlag{Name: "flag"}, }, 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) } return errors.New("intercepted: " + err.Error()) }, }, } err := app.Run([]string{"foo", "bar", "--flag=wrong"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { t.Errorf("Expect an intercepted error, but got \"%v\"", err) } } func TestCommand_OnUsageError_WithSubcommand(t *testing.T) { app := NewApp() app.Commands = []Command{ { Name: "bar", Subcommands: []Command{ { Name: "baz", }, }, Flags: []Flag{ IntFlag{Name: "flag"}, }, 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) } return errors.New("intercepted: " + err.Error()) }, }, } err := app.Run([]string{"foo", "bar", "--flag=wrong"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { t.Errorf("Expect an intercepted error, but got \"%v\"", err) } } func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) { app := NewApp() app.ErrWriter = ioutil.Discard app.Commands = []Command{ { Name: "bar", Usage: "this is for testing", Subcommands: []Command{ { Name: "baz", Usage: "this is for testing", Action: func(c *Context) error { if c.App.ErrWriter != ioutil.Discard { return fmt.Errorf("ErrWriter not passed") } return nil }, }, }, }, } err := app.Run([]string{"foo", "bar", "baz"}) if err != nil { t.Fatal(err) } } ================================================ FILE: vendor/github.com/codegangsta/cli/context.go ================================================ package cli import ( "errors" "flag" "reflect" "strings" "syscall" ) // Context is a type that is passed through to // each Handler action in a cli application. Context // can be used to retrieve context-specific Args and // parsed command-line options. type Context struct { App *App Command Command shellComplete bool flagSet *flag.FlagSet setFlags map[string]bool parentContext *Context } // 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 { c := &Context{App: app, flagSet: set, parentContext: parentCtx} if parentCtx != nil { c.shellComplete = parentCtx.shellComplete } return c } // NumFlags returns the number of flags set func (c *Context) NumFlags() int { return c.flagSet.NFlag() } // Set sets a context flag to a value. func (c *Context) Set(name, value string) error { c.setFlags = nil 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 { globalContext(c).setFlags = nil 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) c.flagSet.Visit(func(f *flag.Flag) { c.setFlags[f.Name] = true }) c.flagSet.VisitAll(func(f *flag.Flag) { if _, ok := c.setFlags[f.Name]; ok { return } c.setFlags[f.Name] = false }) // XXX hack to support IsSet for flags with EnvVar // // There isn't an easy way to do this with the current implementation since // whether a flag was set via an environment variable is very difficult to // determine here. Instead, we intend to introduce a backwards incompatible // change in version 2 to add `IsSet` to the Flag interface to push the // responsibility closer to where the information required to determine // whether a flag is set by non-standard means such as environment // variables is avaliable. // // See https://github.com/urfave/cli/issues/294 for additional discussion flags := c.Command.Flags if c.Command.Name == "" { // cannot == Command{} since it contains slice types if c.App != nil { flags = c.App.Flags } } for _, f := range flags { eachName(f.GetName(), func(name string) { if isSet, ok := c.setFlags[name]; isSet || !ok { return } val := reflect.ValueOf(f) if val.Kind() == reflect.Ptr { val = val.Elem() } envVarValue := val.FieldByName("EnvVar") if !envVarValue.IsValid() { return } eachName(envVarValue.String(), func(envVar string) { envVar = strings.TrimSpace(envVar) if _, ok := syscall.Getenv(envVar); ok { c.setFlags[name] = true return } }) }) } } return c.setFlags[name] } // GlobalIsSet determines if the global flag was actually set func (c *Context) GlobalIsSet(name string) bool { ctx := c if ctx.parentContext != nil { ctx = ctx.parentContext } for ; ctx != nil; ctx = ctx.parentContext { if ctx.IsSet(name) { return true } } return false } // 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] if name == "help" { continue } names = append(names, name) } return } // 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] if name == "help" || name == "version" { continue } names = append(names, name) } return } // Parent returns the parent context, if any func (c *Context) Parent() *Context { return c.parentContext } // value returns the value of the flag coressponding to `name` func (c *Context) value(name string) interface{} { return c.flagSet.Lookup(name).Value.(flag.Getter).Get() } // Args contains apps console arguments type Args []string // Args returns the command line arguments associated with the context. func (c *Context) Args() Args { args := Args(c.flagSet.Args()) return args } // NArg returns the number of the command line arguments. func (c *Context) NArg() int { return len(c.Args()) } // Get returns the nth argument, or else a blank string func (a Args) Get(n int) string { if len(a) > n { return a[n] } return "" } // First returns the first argument, or else a blank string func (a Args) First() string { return a.Get(0) } // 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 { return []string(a)[1:] } return []string{} } // Present checks if there are any arguments present func (a Args) Present() bool { return len(a) != 0 } // 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") } a[from], a[to] = a[to], a[from] 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 } for ; ctx != nil; ctx = ctx.parentContext { if f := ctx.flagSet.Lookup(name); f != nil { return ctx.flagSet } } return nil } func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { switch ff.Value.(type) { case *StringSlice: default: set.Set(name, ff.Value.String()) } } func normalizeFlags(flags []Flag, set *flag.FlagSet) error { visited := make(map[string]bool) set.Visit(func(f *flag.Flag) { visited[f.Name] = true }) for _, f := range flags { parts := strings.Split(f.GetName(), ",") if len(parts) == 1 { continue } var ff *flag.Flag for _, name := range parts { name = strings.Trim(name, " ") if visited[name] { if ff != nil { return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) } ff = set.Lookup(name) } } if ff == nil { continue } for _, name := range parts { name = strings.Trim(name, " ") if !visited[name] { copyFlag(name, ff, set) } } } return nil } ================================================ FILE: vendor/github.com/codegangsta/cli/context_test.go ================================================ package cli import ( "flag" "os" "testing" "time" ) 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") } func TestContext_Int(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") c := NewContext(nil, set, nil) 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") c := NewContext(nil, set, nil) expect(t, c.Duration("myflag"), time.Duration(12*time.Second)) } func TestContext_String(t *testing.T) { set := flag.NewFlagSet("test", 0) set.String("myflag", "hello world", "doc") c := NewContext(nil, set, nil) expect(t, c.String("myflag"), "hello world") } func TestContext_Bool(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") c := NewContext(nil, set, nil) expect(t, c.Bool("myflag"), false) } func TestContext_BoolT(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", true, "doc") c := NewContext(nil, set, nil) 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") c := NewContext(nil, set, nil) set.Parse([]string{"--myflag", "bat", "baz"}) expect(t, len(c.Args()), 2) expect(t, c.Bool("myflag"), true) } func TestContext_NArg(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") c := NewContext(nil, set, nil) set.Parse([]string{"--myflag", "bat", "baz"}) expect(t, c.NArg(), 2) } func TestContext_IsSet(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") set.String("otherflag", "hello world", "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Bool("myflagGlobal", true, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) set.Parse([]string{"--myflag", "bat", "baz"}) globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) expect(t, c.IsSet("myflag"), true) expect(t, c.IsSet("otherflag"), false) expect(t, c.IsSet("bogusflag"), false) expect(t, c.IsSet("myflagGlobal"), false) } // XXX Corresponds to hack in context.IsSet for flags with EnvVar field // Should be moved to `flag_test` in v2 func TestContext_IsSet_fromEnv(t *testing.T) { var ( timeoutIsSet, tIsSet bool noEnvVarIsSet, nIsSet bool passwordIsSet, pIsSet bool unparsableIsSet, uIsSet bool ) clearenv() os.Setenv("APP_TIMEOUT_SECONDS", "15.5") os.Setenv("APP_PASSWORD", "") a := App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"}, Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"}, Float64Flag{Name: "no-env-var, n"}, }, Action: func(ctx *Context) error { timeoutIsSet = ctx.IsSet("timeout") tIsSet = ctx.IsSet("t") passwordIsSet = ctx.IsSet("password") pIsSet = ctx.IsSet("p") unparsableIsSet = ctx.IsSet("unparsable") uIsSet = ctx.IsSet("u") noEnvVarIsSet = ctx.IsSet("no-env-var") nIsSet = ctx.IsSet("n") return nil }, } a.Run([]string{"run"}) expect(t, timeoutIsSet, true) expect(t, tIsSet, true) expect(t, passwordIsSet, true) expect(t, pIsSet, true) expect(t, noEnvVarIsSet, false) expect(t, nIsSet, false) os.Setenv("APP_UNPARSABLE", "foobar") a.Run([]string{"run"}) expect(t, unparsableIsSet, false) expect(t, uIsSet, false) } func TestContext_GlobalIsSet(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") set.String("otherflag", "hello world", "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Bool("myflagGlobal", true, "doc") globalSet.Bool("myflagGlobalUnset", true, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) set.Parse([]string{"--myflag", "bat", "baz"}) globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) expect(t, c.GlobalIsSet("myflag"), false) expect(t, c.GlobalIsSet("otherflag"), false) expect(t, c.GlobalIsSet("bogusflag"), false) expect(t, c.GlobalIsSet("myflagGlobal"), true) expect(t, c.GlobalIsSet("myflagGlobalUnset"), false) expect(t, c.GlobalIsSet("bogusGlobal"), false) } // XXX Corresponds to hack in context.IsSet for flags with EnvVar field // Should be moved to `flag_test` in v2 func TestContext_GlobalIsSet_fromEnv(t *testing.T) { var ( timeoutIsSet, tIsSet bool noEnvVarIsSet, nIsSet bool passwordIsSet, pIsSet bool unparsableIsSet, uIsSet bool ) clearenv() os.Setenv("APP_TIMEOUT_SECONDS", "15.5") os.Setenv("APP_PASSWORD", "") a := App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"}, Float64Flag{Name: "no-env-var, n"}, Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"}, }, Commands: []Command{ { Name: "hello", Action: func(ctx *Context) error { timeoutIsSet = ctx.GlobalIsSet("timeout") tIsSet = ctx.GlobalIsSet("t") passwordIsSet = ctx.GlobalIsSet("password") pIsSet = ctx.GlobalIsSet("p") unparsableIsSet = ctx.GlobalIsSet("unparsable") uIsSet = ctx.GlobalIsSet("u") noEnvVarIsSet = ctx.GlobalIsSet("no-env-var") nIsSet = ctx.GlobalIsSet("n") return nil }, }, }, } if err := a.Run([]string{"run", "hello"}); err != nil { t.Logf("error running Run(): %+v", err) } expect(t, timeoutIsSet, true) expect(t, tIsSet, true) expect(t, passwordIsSet, true) expect(t, pIsSet, true) expect(t, noEnvVarIsSet, false) expect(t, nIsSet, false) os.Setenv("APP_UNPARSABLE", "foobar") if err := a.Run([]string{"run"}); err != nil { t.Logf("error running Run(): %+v", err) } expect(t, unparsableIsSet, false) expect(t, uIsSet, false) } func TestContext_NumFlags(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") set.String("otherflag", "hello world", "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Bool("myflagGlobal", true, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) set.Parse([]string{"--myflag", "--otherflag=foo"}) 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) expect(t, c.IsSet("int"), false) c.Set("int", "1") expect(t, c.Int("int"), 1) expect(t, c.IsSet("int"), true) } 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) expect(t, c.GlobalIsSet("int"), false) c.GlobalSet("int", "1") expect(t, c.Int("int"), 1) expect(t, c.GlobalInt("int"), 1) expect(t, c.GlobalIsSet("int"), true) } ================================================ FILE: vendor/github.com/codegangsta/cli/errors.go ================================================ 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 implements 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") } type ErrorFormatter interface { Format(s fmt.State, verb rune) } // 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 interface{} } // NewExitError makes a new *ExitError func NewExitError(message interface{}, 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 fmt.Sprintf("%v", 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 and calls OsExiter with the last exit code. func HandleExitCoder(err error) { if err == nil { return } if exitErr, ok := err.(ExitCoder); ok { if err.Error() != "" { if _, ok := exitErr.(ErrorFormatter); ok { fmt.Fprintf(ErrWriter, "%+v\n", err) } else { fmt.Fprintln(ErrWriter, err) } } OsExiter(exitErr.ExitCode()) return } if multiErr, ok := err.(MultiError); ok { code := handleMultiError(multiErr) OsExiter(code) return } } func handleMultiError(multiErr MultiError) int { code := 1 for _, merr := range multiErr.Errors { if multiErr2, ok := merr.(MultiError); ok { code = handleMultiError(multiErr2) } else { fmt.Fprintln(ErrWriter, merr) if exitErr, ok := merr.(ExitCoder); ok { code = exitErr.ExitCode() } } } return code } ================================================ FILE: vendor/github.com/codegangsta/cli/errors_test.go ================================================ package cli import ( "bytes" "errors" "fmt" "testing" ) func TestHandleExitCoder_nil(t *testing.T) { exitCode := 0 called := false OsExiter = func(rc int) { if !called { exitCode = rc called = true } } defer func() { OsExiter = fakeOsExiter }() HandleExitCoder(nil) expect(t, exitCode, 0) expect(t, called, false) } func TestHandleExitCoder_ExitCoder(t *testing.T) { exitCode := 0 called := false OsExiter = func(rc int) { if !called { exitCode = rc called = true } } defer func() { OsExiter = fakeOsExiter }() 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) { if !called { exitCode = rc called = true } } defer func() { OsExiter = fakeOsExiter }() exitErr := NewExitError("galactic perimeter breach", 9) exitErr2 := NewExitError("last ExitCoder", 11) err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr, exitErr2) HandleExitCoder(err) expect(t, exitCode, 11) expect(t, called, true) } // make a stub to not import pkg/errors type ErrorWithFormat struct { error } func NewErrorWithFormat(m string) *ErrorWithFormat { return &ErrorWithFormat{error: errors.New(m)} } func (f *ErrorWithFormat) Format(s fmt.State, verb rune) { fmt.Fprintf(s, "This the format: %v", f.error) } func TestHandleExitCoder_ErrorWithFormat(t *testing.T) { called := false OsExiter = func(rc int) { if !called { called = true } } ErrWriter = &bytes.Buffer{} defer func() { OsExiter = fakeOsExiter ErrWriter = fakeErrWriter }() err := NewExitError(NewErrorWithFormat("I am formatted"), 1) HandleExitCoder(err) expect(t, called, true) expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: I am formatted\n") } func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) { called := false OsExiter = func(rc int) { if !called { called = true } } ErrWriter = &bytes.Buffer{} defer func() { OsExiter = fakeOsExiter }() err := NewMultiError(NewErrorWithFormat("err1"), NewErrorWithFormat("err2")) HandleExitCoder(err) expect(t, called, true) expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: err1\nThis the format: err2\n") } ================================================ FILE: vendor/github.com/codegangsta/cli/flag-types.json ================================================ [ { "name": "Bool", "type": "bool", "value": false, "context_default": "false", "parser": "strconv.ParseBool(f.Value.String())" }, { "name": "BoolT", "type": "bool", "value": false, "doctail": " that is true by default", "context_default": "false", "parser": "strconv.ParseBool(f.Value.String())" }, { "name": "Duration", "type": "time.Duration", "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", "context_default": "0", "parser": "time.ParseDuration(f.Value.String())" }, { "name": "Float64", "type": "float64", "context_default": "0", "parser": "strconv.ParseFloat(f.Value.String(), 64)" }, { "name": "Generic", "type": "Generic", "dest": false, "context_default": "nil", "context_type": "interface{}" }, { "name": "Int64", "type": "int64", "context_default": "0", "parser": "strconv.ParseInt(f.Value.String(), 0, 64)" }, { "name": "Int", "type": "int", "context_default": "0", "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", "parser_cast": "int(parsed)" }, { "name": "IntSlice", "type": "*IntSlice", "dest": false, "context_default": "nil", "context_type": "[]int", "parser": "(f.Value.(*IntSlice)).Value(), error(nil)" }, { "name": "Int64Slice", "type": "*Int64Slice", "dest": false, "context_default": "nil", "context_type": "[]int64", "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)" }, { "name": "String", "type": "string", "context_default": "\"\"", "parser": "f.Value.String(), error(nil)" }, { "name": "StringSlice", "type": "*StringSlice", "dest": false, "context_default": "nil", "context_type": "[]string", "parser": "(f.Value.(*StringSlice)).Value(), error(nil)" }, { "name": "Uint64", "type": "uint64", "context_default": "0", "parser": "strconv.ParseUint(f.Value.String(), 0, 64)" }, { "name": "Uint", "type": "uint", "context_default": "0", "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", "parser_cast": "uint(parsed)" } ] ================================================ FILE: vendor/github.com/codegangsta/cli/flag.go ================================================ package cli import ( "flag" "fmt" "reflect" "runtime" "strconv" "strings" "syscall" "time" ) const defaultPlaceholder = "value" // BashCompletionFlag enables bash-completion for all commands and subcommands var BashCompletionFlag Flag = BoolFlag{ Name: "generate-bash-completion", Hidden: true, } // VersionFlag prints the version for the application var VersionFlag Flag = BoolFlag{ Name: "version, v", Usage: "print the version", } // 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 Flag = BoolFlag{ Name: "help, h", Usage: "show help", } // FlagStringer converts a flag definition to a string. This is used by help // to display a flag. var FlagStringer FlagStringFunc = stringifyFlag // FlagsByName is a slice of Flag. type FlagsByName []Flag func (f FlagsByName) Len() int { return len(f) } func (f FlagsByName) Less(i, j int) bool { return f[i].GetName() < f[j].GetName() } func (f FlagsByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } // 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. type Flag interface { fmt.Stringer // Apply Flag settings to the given flag set Apply(*flag.FlagSet) GetName() string } // errorableFlag is an interface that allows us to return errors during apply // it allows flags defined in this library to return errors in a fashion backwards compatible // TODO remove in v2 and modify the existing Flag interface to return errors type errorableFlag interface { Flag ApplyWithError(*flag.FlagSet) error } func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { set := flag.NewFlagSet(name, flag.ContinueOnError) for _, f := range flags { //TODO remove in v2 when errorableFlag is removed if ef, ok := f.(errorableFlag); ok { if err := ef.ApplyWithError(set); err != nil { return nil, err } } else { f.Apply(set) } } return set, nil } func eachName(longName string, fn func(string)) { parts := strings.Split(longName, ",") for _, name := range parts { name = strings.Trim(name, " ") fn(name) } } // Generic is a generic parseable type identified by a specific flag type Generic interface { Set(value string) error String() string } // Apply takes the flagset and calls Set on the generic flag with the value // provided by the user for parsing by the flag // Ignores parsing errors func (f GenericFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError takes the flagset and calls Set on the generic flag with the value // provided by the user for parsing by the flag func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { val := f.Value if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { if err := val.Set(envVal); err != nil { return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err) } break } } } eachName(f.Name, func(name string) { set.Var(f.Value, name, f.Usage) }) return nil } // StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter type StringSlice []string // Set appends the string value to the list of values func (f *StringSlice) Set(value string) error { *f = append(*f, value) return nil } // String returns a readable representation of this value (for usage defaults) func (f *StringSlice) String() string { return fmt.Sprintf("%s", *f) } // Value returns the slice of strings set by this flag func (f *StringSlice) Value() []string { return *f } // Get returns the slice of strings set by this flag func (f *StringSlice) Get() interface{} { return *f } // Apply populates the flag given the flag set and environment // Ignores errors func (f StringSliceFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { newVal := &StringSlice{} for _, s := range strings.Split(envVal, ",") { s = strings.TrimSpace(s) if err := newVal.Set(s); err != nil { return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) } } f.Value = newVal break } } } eachName(f.Name, func(name string) { if f.Value == nil { f.Value = &StringSlice{} } set.Var(f.Value, name, f.Usage) }) return nil } // IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter type IntSlice []int // Set parses the value into an integer and appends it to the list of values func (f *IntSlice) Set(value string) error { tmp, err := strconv.Atoi(value) if err != nil { return err } *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("%#v", *f) } // Value returns the slice of ints set by this flag func (f *IntSlice) Value() []int { return *f } // Get returns the slice of ints set by this flag func (f *IntSlice) Get() interface{} { return *f } // Apply populates the flag given the flag set and environment // Ignores errors func (f IntSliceFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { newVal := &IntSlice{} for _, s := range strings.Split(envVal, ",") { s = strings.TrimSpace(s) if err := newVal.Set(s); err != nil { return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) } } f.Value = newVal break } } } eachName(f.Name, func(name string) { if f.Value == nil { f.Value = &IntSlice{} } set.Var(f.Value, name, f.Usage) }) return nil } // Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter 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 } // Get returns the slice of ints set by this flag func (f *Int64Slice) Get() interface{} { return *f } // Apply populates the flag given the flag set and environment // Ignores errors func (f Int64SliceFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { newVal := &Int64Slice{} for _, s := range strings.Split(envVal, ",") { s = strings.TrimSpace(s) if err := newVal.Set(s); err != nil { return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) } } f.Value = newVal break } } } eachName(f.Name, func(name string) { if f.Value == nil { f.Value = &Int64Slice{} } set.Var(f.Value, name, f.Usage) }) return nil } // Apply populates the flag given the flag set and environment // Ignores errors func (f BoolFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { val := false if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { if envVal == "" { val = false break } envValBool, err := strconv.ParseBool(envVal) if err != nil { return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) } val = envValBool break } } } eachName(f.Name, func(name string) { if f.Destination != nil { set.BoolVar(f.Destination, name, val, f.Usage) return } set.Bool(name, val, f.Usage) }) return nil } // Apply populates the flag given the flag set and environment // Ignores errors func (f BoolTFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { val := true if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { if envVal == "" { val = false break } envValBool, err := strconv.ParseBool(envVal) if err != nil { return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) } val = envValBool break } } } eachName(f.Name, func(name string) { if f.Destination != nil { set.BoolVar(f.Destination, name, val, f.Usage) return } set.Bool(name, val, f.Usage) }) return nil } // Apply populates the flag given the flag set and environment // Ignores errors func (f StringFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { f.Value = envVal break } } } eachName(f.Name, func(name string) { if f.Destination != nil { set.StringVar(f.Destination, name, f.Value, f.Usage) return } set.String(name, f.Value, f.Usage) }) return nil } // Apply populates the flag given the flag set and environment // Ignores errors func (f IntFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { envValInt, err := strconv.ParseInt(envVal, 0, 64) if err != nil { return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) } f.Value = int(envValInt) break } } } eachName(f.Name, func(name string) { if f.Destination != nil { set.IntVar(f.Destination, name, f.Value, f.Usage) return } set.Int(name, f.Value, f.Usage) }) return nil } // Apply populates the flag given the flag set and environment // Ignores errors func (f Int64Flag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { envValInt, err := strconv.ParseInt(envVal, 0, 64) if err != nil { return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) } 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) }) return nil } // Apply populates the flag given the flag set and environment // Ignores errors func (f UintFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { envValInt, err := strconv.ParseUint(envVal, 0, 64) if err != nil { return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) } 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) }) return nil } // Apply populates the flag given the flag set and environment // Ignores errors func (f Uint64Flag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { envValInt, err := strconv.ParseUint(envVal, 0, 64) if err != nil { return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) } 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) }) return nil } // Apply populates the flag given the flag set and environment // Ignores errors func (f DurationFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { envValDuration, err := time.ParseDuration(envVal) if err != nil { return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) } f.Value = envValDuration break } } } eachName(f.Name, func(name string) { if f.Destination != nil { set.DurationVar(f.Destination, name, f.Value, f.Usage) return } set.Duration(name, f.Value, f.Usage) }) return nil } // Apply populates the flag given the flag set and environment // Ignores errors func (f Float64Flag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { envValFloat, err := strconv.ParseFloat(envVal, 10) if err != nil { return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) } f.Value = float64(envValFloat) break } } } eachName(f.Name, func(name string) { if f.Destination != nil { set.Float64Var(f.Destination, name, f.Value, f.Usage) return } set.Float64(name, f.Value, f.Usage) }) return nil } func visibleFlags(fl []Flag) []Flag { visible := []Flag{} for _, flag := range fl { field := flagValue(flag).FieldByName("Hidden") if !field.IsValid() || !field.Bool() { visible = append(visible, flag) } } return visible } func prefixFor(name string) (prefix string) { if len(name) == 1 { prefix = "-" } else { prefix = "--" } return } // 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 prefixed } func withEnvHint(envVar, str string) string { envText := "" if envVar != "" { prefix := "$" suffix := "" sep := ", $" if runtime.GOOS == "windows" { prefix = "%" suffix = "%" sep = "%, %" } envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix) } 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 := "" if val := fv.FieldByName("Value"); 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) } ================================================ FILE: vendor/github.com/codegangsta/cli/flag_generated.go ================================================ package cli import ( "flag" "strconv" "time" ) // WARNING: This file is generated! // BoolFlag is a flag with type bool type BoolFlag struct { Name string Usage string EnvVar string Hidden bool Destination *bool } // String returns a readable representation of this value // (for usage defaults) func (f BoolFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f BoolFlag) GetName() string { return f.Name } // Bool looks up the value of a local BoolFlag, returns // false if not found func (c *Context) Bool(name string) bool { return lookupBool(name, c.flagSet) } // GlobalBool looks up the value of a global BoolFlag, returns // false if not found func (c *Context) GlobalBool(name string) bool { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupBool(name, fs) } return false } func lookupBool(name string, set *flag.FlagSet) bool { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseBool(f.Value.String()) if err != nil { return false } return parsed } return false } // BoolTFlag is a flag with type bool that is true by default type BoolTFlag struct { Name string Usage string EnvVar string Hidden bool Destination *bool } // String returns a readable representation of this value // (for usage defaults) func (f BoolTFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f BoolTFlag) GetName() string { return f.Name } // BoolT looks up the value of a local BoolTFlag, returns // false if not found func (c *Context) BoolT(name string) bool { return lookupBoolT(name, c.flagSet) } // GlobalBoolT looks up the value of a global BoolTFlag, returns // false if not found func (c *Context) GlobalBoolT(name string) bool { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupBoolT(name, fs) } return false } func lookupBoolT(name string, set *flag.FlagSet) bool { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseBool(f.Value.String()) if err != nil { return false } return parsed } return false } // DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) type DurationFlag struct { Name string Usage string EnvVar string Hidden bool Value time.Duration Destination *time.Duration } // String returns a readable representation of this value // (for usage defaults) func (f DurationFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f DurationFlag) GetName() string { return f.Name } // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (c *Context) Duration(name string) time.Duration { return lookupDuration(name, c.flagSet) } // GlobalDuration looks up the value of a global DurationFlag, returns // 0 if not found func (c *Context) GlobalDuration(name string) time.Duration { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupDuration(name, fs) } return 0 } func lookupDuration(name string, set *flag.FlagSet) time.Duration { f := set.Lookup(name) if f != nil { parsed, err := time.ParseDuration(f.Value.String()) if err != nil { return 0 } return parsed } return 0 } // Float64Flag is a flag with type float64 type Float64Flag struct { Name string Usage string EnvVar string Hidden bool Value float64 Destination *float64 } // String returns a readable representation of this value // (for usage defaults) func (f Float64Flag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f Float64Flag) GetName() string { return f.Name } // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (c *Context) Float64(name string) float64 { return lookupFloat64(name, c.flagSet) } // GlobalFloat64 looks up the value of a global Float64Flag, returns // 0 if not found func (c *Context) GlobalFloat64(name string) float64 { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupFloat64(name, fs) } return 0 } func lookupFloat64(name string, set *flag.FlagSet) float64 { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseFloat(f.Value.String(), 64) if err != nil { return 0 } return parsed } return 0 } // GenericFlag is a flag with type Generic type GenericFlag struct { Name string Usage string EnvVar string Hidden bool Value Generic } // String returns a readable representation of this value // (for usage defaults) func (f GenericFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f GenericFlag) GetName() string { return f.Name } // Generic looks up the value of a local GenericFlag, returns // nil if not found func (c *Context) Generic(name string) interface{} { return lookupGeneric(name, c.flagSet) } // GlobalGeneric looks up the value of a global GenericFlag, returns // nil if not found func (c *Context) GlobalGeneric(name string) interface{} { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupGeneric(name, fs) } return nil } func lookupGeneric(name string, set *flag.FlagSet) interface{} { f := set.Lookup(name) if f != nil { parsed, err := f.Value, error(nil) if err != nil { return nil } return parsed } return nil } // Int64Flag is a flag with type int64 type Int64Flag struct { Name string Usage string EnvVar string Hidden bool Value int64 Destination *int64 } // String returns a readable representation of this value // (for usage defaults) func (f Int64Flag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f Int64Flag) GetName() string { return f.Name } // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (c *Context) Int64(name string) int64 { return lookupInt64(name, c.flagSet) } // GlobalInt64 looks up the value of a global Int64Flag, returns // 0 if not found func (c *Context) GlobalInt64(name string) int64 { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupInt64(name, fs) } return 0 } func lookupInt64(name string, set *flag.FlagSet) int64 { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) if err != nil { return 0 } return parsed } return 0 } // IntFlag is a flag with type int type IntFlag struct { Name string Usage string EnvVar string Hidden bool Value int Destination *int } // String returns a readable representation of this value // (for usage defaults) func (f IntFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f IntFlag) GetName() string { return f.Name } // Int looks up the value of a local IntFlag, returns // 0 if not found func (c *Context) Int(name string) int { return lookupInt(name, c.flagSet) } // GlobalInt looks up the value of a global IntFlag, returns // 0 if not found func (c *Context) GlobalInt(name string) int { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupInt(name, fs) } return 0 } func lookupInt(name string, set *flag.FlagSet) int { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) if err != nil { return 0 } return int(parsed) } return 0 } // IntSliceFlag is a flag with type *IntSlice type IntSliceFlag struct { Name string Usage string EnvVar string Hidden bool Value *IntSlice } // String returns a readable representation of this value // (for usage defaults) func (f IntSliceFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f IntSliceFlag) GetName() string { return f.Name } // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (c *Context) IntSlice(name string) []int { return lookupIntSlice(name, c.flagSet) } // GlobalIntSlice looks up the value of a global IntSliceFlag, returns // nil if not found func (c *Context) GlobalIntSlice(name string) []int { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupIntSlice(name, fs) } return nil } func lookupIntSlice(name string, set *flag.FlagSet) []int { f := set.Lookup(name) if f != nil { parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) if err != nil { return nil } return parsed } return nil } // Int64SliceFlag is a flag with type *Int64Slice type Int64SliceFlag struct { Name string Usage string EnvVar string Hidden bool Value *Int64Slice } // String returns a readable representation of this value // (for usage defaults) func (f Int64SliceFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f Int64SliceFlag) GetName() string { return f.Name } // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (c *Context) Int64Slice(name string) []int64 { return lookupInt64Slice(name, c.flagSet) } // GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns // nil if not found func (c *Context) GlobalInt64Slice(name string) []int64 { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupInt64Slice(name, fs) } return nil } func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { f := set.Lookup(name) if f != nil { parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) if err != nil { return nil } return parsed } return nil } // StringFlag is a flag with type string type StringFlag struct { Name string Usage string EnvVar string Hidden bool Value string Destination *string } // String returns a readable representation of this value // (for usage defaults) func (f StringFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f StringFlag) GetName() string { return f.Name } // String looks up the value of a local StringFlag, returns // "" if not found func (c *Context) String(name string) string { return lookupString(name, c.flagSet) } // GlobalString looks up the value of a global StringFlag, returns // "" if not found func (c *Context) GlobalString(name string) string { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupString(name, fs) } return "" } func lookupString(name string, set *flag.FlagSet) string { f := set.Lookup(name) if f != nil { parsed, err := f.Value.String(), error(nil) if err != nil { return "" } return parsed } return "" } // StringSliceFlag is a flag with type *StringSlice type StringSliceFlag struct { Name string Usage string EnvVar string Hidden bool Value *StringSlice } // String returns a readable representation of this value // (for usage defaults) func (f StringSliceFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f StringSliceFlag) GetName() string { return f.Name } // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (c *Context) StringSlice(name string) []string { return lookupStringSlice(name, c.flagSet) } // GlobalStringSlice looks up the value of a global StringSliceFlag, returns // nil if not found func (c *Context) GlobalStringSlice(name string) []string { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupStringSlice(name, fs) } return nil } func lookupStringSlice(name string, set *flag.FlagSet) []string { f := set.Lookup(name) if f != nil { parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) if err != nil { return nil } return parsed } return nil } // Uint64Flag is a flag with type uint64 type Uint64Flag struct { Name string Usage string EnvVar string Hidden bool Value uint64 Destination *uint64 } // String returns a readable representation of this value // (for usage defaults) func (f Uint64Flag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f Uint64Flag) GetName() string { return f.Name } // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found func (c *Context) Uint64(name string) uint64 { return lookupUint64(name, c.flagSet) } // GlobalUint64 looks up the value of a global Uint64Flag, returns // 0 if not found func (c *Context) GlobalUint64(name string) uint64 { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupUint64(name, fs) } return 0 } func lookupUint64(name string, set *flag.FlagSet) uint64 { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) if err != nil { return 0 } return parsed } return 0 } // UintFlag is a flag with type uint type UintFlag struct { Name string Usage string EnvVar string Hidden bool Value uint Destination *uint } // String returns a readable representation of this value // (for usage defaults) func (f UintFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f UintFlag) GetName() string { return f.Name } // Uint looks up the value of a local UintFlag, returns // 0 if not found func (c *Context) Uint(name string) uint { return lookupUint(name, c.flagSet) } // GlobalUint looks up the value of a global UintFlag, returns // 0 if not found func (c *Context) GlobalUint(name string) uint { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupUint(name, fs) } return 0 } func lookupUint(name string, set *flag.FlagSet) uint { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) if err != nil { return 0 } return uint(parsed) } return 0 } ================================================ FILE: vendor/github.com/codegangsta/cli/flag_test.go ================================================ package cli import ( "fmt" "os" "reflect" "regexp" "runtime" "strings" "testing" "time" ) var boolFlagTests = []struct { name string expected string }{ {"help", "--help\t"}, {"h", "-h\t"}, } func TestBoolFlagHelpOutput(t *testing.T) { for _, test := range boolFlagTests { flag := BoolFlag{Name: test.name} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestFlagsFromEnv(t *testing.T) { var flagTests = []struct { input string output interface{} flag Flag errRegexp string }{ {"", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"1", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"false", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"foobar", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Sprintf(`could not parse foobar as bool value for flag debug: .*`)}, {"", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"1", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"false", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"foobar", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Sprintf(`could not parse foobar as bool value for flag debug: .*`)}, {"1s", 1 * time.Second, DurationFlag{Name: "time", EnvVar: "TIME"}, ""}, {"foobar", false, DurationFlag{Name: "time", EnvVar: "TIME"}, fmt.Sprintf(`could not parse foobar as duration for flag time: .*`)}, {"1.2", 1.2, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1", 1.0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"foobar", 0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as float64 value for flag seconds: .*`)}, {"1", int64(1), Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as int value for flag seconds: .*`)}, {"foobar", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int value for flag seconds: .*`)}, {"1", 1, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as int value for flag seconds: .*`)}, {"foobar", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int value for flag seconds: .*`)}, {"1,2", IntSlice{1, 2}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2,2", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2,2 as int slice value for flag seconds: .*`)}, {"foobar", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int slice value for flag seconds: .*`)}, {"1,2", Int64Slice{1, 2}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2,2", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2,2 as int64 slice value for flag seconds: .*`)}, {"foobar", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int64 slice value for flag seconds: .*`)}, {"foo", "foo", StringFlag{Name: "name", EnvVar: "NAME"}, ""}, {"foo,bar", StringSlice{"foo", "bar"}, StringSliceFlag{Name: "names", EnvVar: "NAMES"}, ""}, {"1", uint(1), UintFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as uint value for flag seconds: .*`)}, {"foobar", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as uint value for flag seconds: .*`)}, {"1", uint64(1), Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as uint64 value for flag seconds: .*`)}, {"foobar", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as uint64 value for flag seconds: .*`)}, {"foo,bar", &Parser{"foo", "bar"}, GenericFlag{Name: "names", Value: &Parser{}, EnvVar: "NAMES"}, ""}, } for _, test := range flagTests { os.Clearenv() os.Setenv(reflect.ValueOf(test.flag).FieldByName("EnvVar").String(), test.input) a := App{ Flags: []Flag{test.flag}, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.value(test.flag.GetName()), test.output) { t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.value(test.flag.GetName())) } return nil }, } err := a.Run([]string{"run"}) if test.errRegexp != "" { if err == nil { t.Errorf("expected error to match %s, got none", test.errRegexp) } else { if matched, _ := regexp.MatchString(test.errRegexp, err.Error()); !matched { t.Errorf("expected error to match %s, got error %s", test.errRegexp, err) } } } else { if err != nil && test.errRegexp == "" { t.Errorf("expected no error got %s", err) } } } } var stringFlagTests = []struct { name string usage string value string expected string }{ {"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, Usage: test.usage, Value: test.value} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_FOO", "derp") for _, test := range stringFlagTests { flag := StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"} output := flag.String() expectedSuffix := " [$APP_FOO]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_FOO%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } var stringSliceFlagTests = []struct { name string value *StringSlice expected string }{ {"foo", func() *StringSlice { s := &StringSlice{} s.Set("") return s }(), "--foo value\t"}, {"f", func() *StringSlice { s := &StringSlice{} s.Set("") return s }(), "-f value\t"}, {"f", func() *StringSlice { s := &StringSlice{} s.Set("Lipstick") return s }(), "-f value\t(default: \"Lipstick\")"}, {"test", func() *StringSlice { s := &StringSlice{} s.Set("Something") return s }(), "--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() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_QWWX", "11,4") for _, test := range stringSliceFlagTests { flag := StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"} output := flag.String() expectedSuffix := " [$APP_QWWX]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_QWWX%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%q does not end with"+expectedSuffix, output) } } } var intFlagTests = []struct { name string expected string }{ {"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, Value: 9} output := flag.String() if output != test.expected { t.Errorf("%s does not match %s", output, test.expected) } } } func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAR", "2") for _, test := range intFlagTests { 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 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) } } } var durationFlagTests = []struct { name string expected string }{ {"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, Value: 1 * time.Second} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAR", "2h3m6s") for _, test := range durationFlagTests { flag := DurationFlag{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 intSliceFlagTests = []struct { name string value *IntSlice expected string }{ {"heads", &IntSlice{}, "--heads value\t"}, {"H", &IntSlice{}, "-H value\t"}, {"H, heads", func() *IntSlice { i := &IntSlice{} i.Set("9") i.Set("3") return i }(), "-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() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_SMURF", "42,3") for _, test := range intSliceFlagTests { flag := IntSliceFlag{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) } } } 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) } } } var float64FlagTests = []struct { name string expected string }{ {"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, Value: float64(0.1)} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAZ", "99.4") for _, test := range float64FlagTests { flag := Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} output := flag.String() expectedSuffix := " [$APP_BAZ]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_BAZ%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } var genericFlagTests = []struct { name string value Generic expected string }{ {"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() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_ZAP", "3") for _, test := range genericFlagTests { flag := GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} output := flag.String() expectedSuffix := " [$APP_ZAP]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_ZAP%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } func TestParseMultiString(t *testing.T) { (&App{ Flags: []Flag{ StringFlag{Name: "serve, s"}, }, 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"}) } func TestParseDestinationString(t *testing.T) { var dest string a := App{ Flags: []Flag{ StringFlag{ Name: "dest", Destination: &dest, }, }, Action: func(ctx *Context) error { if dest != "10" { t.Errorf("expected destination String 10") } return nil }, } a.Run([]string{"run", "--dest", "10"}) } func TestParseMultiStringFromEnv(t *testing.T) { os.Clearenv() os.Setenv("APP_COUNT", "20") (&App{ Flags: []Flag{ StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, }, 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"}) } func TestParseMultiStringFromEnvCascade(t *testing.T) { os.Clearenv() os.Setenv("APP_COUNT", "20") (&App{ Flags: []Flag{ StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"}, }, 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"}) } func TestParseMultiStringSlice(t *testing.T) { (&App{ Flags: []Flag{ StringSliceFlag{Name: "serve, s", Value: &StringSlice{}}, }, 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"}) } func TestParseMultiStringSliceFromEnv(t *testing.T) { os.Clearenv() os.Setenv("APP_INTERVALS", "20,30,40") (&App{ Flags: []Flag{ StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "APP_INTERVALS"}, }, 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"}) } func TestParseMultiStringSliceFromEnvCascade(t *testing.T) { os.Clearenv() os.Setenv("APP_INTERVALS", "20,30,40") (&App{ Flags: []Flag{ StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, }, 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"}) } func TestParseMultiInt(t *testing.T) { a := App{ Flags: []Flag{ IntFlag{Name: "serve, s"}, }, 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"}) } func TestParseDestinationInt(t *testing.T) { var dest int a := App{ Flags: []Flag{ IntFlag{ Name: "dest", Destination: &dest, }, }, Action: func(ctx *Context) error { if dest != 10 { t.Errorf("expected destination Int 10") } return nil }, } a.Run([]string{"run", "--dest", "10"}) } func TestParseMultiIntFromEnv(t *testing.T) { os.Clearenv() os.Setenv("APP_TIMEOUT_SECONDS", "10") a := App{ Flags: []Flag{ IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, }, 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"}) } func TestParseMultiIntFromEnvCascade(t *testing.T) { os.Clearenv() os.Setenv("APP_TIMEOUT_SECONDS", "10") a := App{ Flags: []Flag{ IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, }, 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"}) } func TestParseMultiIntSlice(t *testing.T) { (&App{ Flags: []Flag{ IntSliceFlag{Name: "serve, s", Value: &IntSlice{}}, }, 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"}) } func TestParseMultiIntSliceFromEnv(t *testing.T) { os.Clearenv() os.Setenv("APP_INTERVALS", "20,30,40") (&App{ Flags: []Flag{ IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "APP_INTERVALS"}, }, 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 TestParseMultiIntSliceFromEnvCascade(t *testing.T) { os.Clearenv() os.Setenv("APP_INTERVALS", "20,30,40") (&App{ Flags: []Flag{ IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, }, 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"}) } func TestParseMultiFloat64(t *testing.T) { a := App{ Flags: []Flag{ Float64Flag{Name: "serve, s"}, }, 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"}) } func TestParseDestinationFloat64(t *testing.T) { var dest float64 a := App{ Flags: []Flag{ Float64Flag{ Name: "dest", Destination: &dest, }, }, 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"}) } func TestParseMultiFloat64FromEnv(t *testing.T) { os.Clearenv() os.Setenv("APP_TIMEOUT_SECONDS", "15.5") a := App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, }, 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"}) } func TestParseMultiFloat64FromEnvCascade(t *testing.T) { os.Clearenv() os.Setenv("APP_TIMEOUT_SECONDS", "15.5") a := App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, }, 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"}) } func TestParseMultiBool(t *testing.T) { a := App{ Flags: []Flag{ BoolFlag{Name: "serve, s"}, }, 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"}) } func TestParseDestinationBool(t *testing.T) { var dest bool a := App{ Flags: []Flag{ BoolFlag{ Name: "dest", Destination: &dest, }, }, Action: func(ctx *Context) error { if dest != true { t.Errorf("expected destination Bool true") } return nil }, } a.Run([]string{"run", "--dest"}) } func TestParseMultiBoolFromEnv(t *testing.T) { os.Clearenv() os.Setenv("APP_DEBUG", "1") a := App{ Flags: []Flag{ BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, }, 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"}) } func TestParseMultiBoolFromEnvCascade(t *testing.T) { os.Clearenv() os.Setenv("APP_DEBUG", "1") a := App{ Flags: []Flag{ BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, }, 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"}) } func TestParseBoolTFromEnv(t *testing.T) { var boolTFlagTests = []struct { input string output bool }{ {"", false}, {"1", true}, {"false", false}, {"true", true}, } for _, test := range boolTFlagTests { os.Clearenv() os.Setenv("DEBUG", test.input) a := App{ Flags: []Flag{ BoolTFlag{Name: "debug, d", EnvVar: "DEBUG"}, }, Action: func(ctx *Context) error { if ctx.Bool("debug") != test.output { t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("debug")) } if ctx.Bool("d") != test.output { t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("d")) } return nil }, } a.Run([]string{"run"}) } } func TestParseMultiBoolT(t *testing.T) { a := App{ Flags: []Flag{ BoolTFlag{Name: "serve, s"}, }, 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"}) } func TestParseDestinationBoolT(t *testing.T) { var dest bool a := App{ Flags: []Flag{ BoolTFlag{ Name: "dest", Destination: &dest, }, }, Action: func(ctx *Context) error { if dest != true { t.Errorf("expected destination BoolT true") } return nil }, } a.Run([]string{"run", "--dest"}) } func TestParseMultiBoolTFromEnv(t *testing.T) { os.Clearenv() os.Setenv("APP_DEBUG", "0") a := App{ Flags: []Flag{ BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, }, 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"}) } func TestParseMultiBoolTFromEnvCascade(t *testing.T) { os.Clearenv() os.Setenv("APP_DEBUG", "0") a := App{ Flags: []Flag{ BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, }, 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"}) } type Parser [2]string func (p *Parser) Set(value string) error { parts := strings.Split(value, ",") if len(parts) != 2 { return fmt.Errorf("invalid format") } (*p)[0] = parts[0] (*p)[1] = parts[1] return nil } func (p *Parser) String() string { return fmt.Sprintf("%s,%s", p[0], p[1]) } func (p *Parser) Get() interface{} { return p } func TestParseGeneric(t *testing.T) { a := App{ Flags: []Flag{ GenericFlag{Name: "serve, s", Value: &Parser{}}, }, 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"}) } func TestParseGenericFromEnv(t *testing.T) { os.Clearenv() os.Setenv("APP_SERVE", "20,30") a := App{ Flags: []Flag{ GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, }, 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"}) } func TestParseGenericFromEnvCascade(t *testing.T) { os.Clearenv() os.Setenv("APP_FOO", "99,2000") a := App{ Flags: []Flag{ GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"}, }, 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"}) } ================================================ FILE: vendor/github.com/codegangsta/cli/funcs.go ================================================ 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 ================================================ FILE: vendor/github.com/codegangsta/cli/generate-flag-types ================================================ #!/usr/bin/env python """ The flag types that ship with the cli library have many things in common, and so we can take advantage of the `go generate` command to create much of the source code from a list of definitions. These definitions attempt to cover the parts that vary between flag types, and should evolve as needed. An example of the minimum definition needed is: { "name": "SomeType", "type": "sometype", "context_default": "nil" } In this example, the code generated for the `cli` package will include a type named `SomeTypeFlag` that is expected to wrap a value of type `sometype`. Fetching values by name via `*cli.Context` will default to a value of `nil`. A more complete, albeit somewhat redundant, example showing all available definition keys is: { "name": "VeryMuchType", "type": "*VeryMuchType", "value": true, "dest": false, "doctail": " which really only wraps a []float64, oh well!", "context_type": "[]float64", "context_default": "nil", "parser": "parseVeryMuchType(f.Value.String())", "parser_cast": "[]float64(parsed)" } The meaning of each field is as follows: name (string) - The type "name", which will be suffixed with `Flag` when generating the type definition for `cli` and the wrapper type for `altsrc` type (string) - The type that the generated `Flag` type for `cli` is expected to "contain" as its `.Value` member value (bool) - Should the generated `cli` type have a `Value` member? dest (bool) - Should the generated `cli` type support a destination pointer? doctail (string) - Additional docs for the `cli` flag type comment context_type (string) - The literal type used in the `*cli.Context` reader func signature context_default (string) - The literal value used as the default by the `*cli.Context` reader funcs when no value is present parser (string) - Literal code used to parse the flag `f`, expected to have a return signature of (value, error) parser_cast (string) - Literal code used to cast the `parsed` value returned from the `parser` code """ from __future__ import print_function, unicode_literals import argparse import json import os import subprocess import sys import tempfile import textwrap class _FancyFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): pass def main(sysargs=sys.argv[:]): parser = argparse.ArgumentParser( description='Generate flag type code!', formatter_class=_FancyFormatter) parser.add_argument( 'package', type=str, default='cli', choices=_WRITEFUNCS.keys(), help='Package for which flag types will be generated' ) parser.add_argument( '-i', '--in-json', type=argparse.FileType('r'), default=sys.stdin, help='Input JSON file which defines each type to be generated' ) parser.add_argument( '-o', '--out-go', type=argparse.FileType('w'), default=sys.stdout, help='Output file/stream to which generated source will be written' ) parser.epilog = __doc__ args = parser.parse_args(sysargs[1:]) _generate_flag_types(_WRITEFUNCS[args.package], args.out_go, args.in_json) return 0 def _generate_flag_types(writefunc, output_go, input_json): types = json.load(input_json) tmp = tempfile.NamedTemporaryFile(suffix='.go', delete=False) writefunc(tmp, types) tmp.close() new_content = subprocess.check_output( ['goimports', tmp.name] ).decode('utf-8') print(new_content, file=output_go, end='') output_go.flush() os.remove(tmp.name) def _set_typedef_defaults(typedef): typedef.setdefault('doctail', '') typedef.setdefault('context_type', typedef['type']) typedef.setdefault('dest', True) typedef.setdefault('value', True) typedef.setdefault('parser', 'f.Value, error(nil)') typedef.setdefault('parser_cast', 'parsed') def _write_cli_flag_types(outfile, types): _fwrite(outfile, """\ package cli // WARNING: This file is generated! """) for typedef in types: _set_typedef_defaults(typedef) _fwrite(outfile, """\ // {name}Flag is a flag with type {type}{doctail} type {name}Flag struct {{ Name string Usage string EnvVar string Hidden bool """.format(**typedef)) if typedef['value']: _fwrite(outfile, """\ Value {type} """.format(**typedef)) if typedef['dest']: _fwrite(outfile, """\ Destination *{type} """.format(**typedef)) _fwrite(outfile, "\n}\n\n") _fwrite(outfile, """\ // String returns a readable representation of this value // (for usage defaults) func (f {name}Flag) String() string {{ return FlagStringer(f) }} // GetName returns the name of the flag func (f {name}Flag) GetName() string {{ return f.Name }} // {name} looks up the value of a local {name}Flag, returns // {context_default} if not found func (c *Context) {name}(name string) {context_type} {{ return lookup{name}(name, c.flagSet) }} // Global{name} looks up the value of a global {name}Flag, returns // {context_default} if not found func (c *Context) Global{name}(name string) {context_type} {{ if fs := lookupGlobalFlagSet(name, c); fs != nil {{ return lookup{name}(name, fs) }} return {context_default} }} func lookup{name}(name string, set *flag.FlagSet) {context_type} {{ f := set.Lookup(name) if f != nil {{ parsed, err := {parser} if err != nil {{ return {context_default} }} return {parser_cast} }} return {context_default} }} """.format(**typedef)) def _write_altsrc_flag_types(outfile, types): _fwrite(outfile, """\ package altsrc import ( "gopkg.in/urfave/cli.v1" ) // WARNING: This file is generated! """) for typedef in types: _set_typedef_defaults(typedef) _fwrite(outfile, """\ // {name}Flag is the flag type that wraps cli.{name}Flag to allow // for other values to be specified type {name}Flag struct {{ cli.{name}Flag set *flag.FlagSet }} // New{name}Flag creates a new {name}Flag func New{name}Flag(fl cli.{name}Flag) *{name}Flag {{ return &{name}Flag{{{name}Flag: fl, set: nil}} }} // Apply saves the flagSet for later usage calls, then calls the // wrapped {name}Flag.Apply func (f *{name}Flag) Apply(set *flag.FlagSet) {{ f.set = set f.{name}Flag.Apply(set) }} // ApplyWithError saves the flagSet for later usage calls, then calls the // wrapped {name}Flag.ApplyWithError func (f *{name}Flag) ApplyWithError(set *flag.FlagSet) error {{ f.set = set return f.{name}Flag.ApplyWithError(set) }} """.format(**typedef)) def _fwrite(outfile, text): print(textwrap.dedent(text), end='', file=outfile) _WRITEFUNCS = { 'cli': _write_cli_flag_types, 'altsrc': _write_altsrc_flag_types } if __name__ == '__main__': sys.exit(main()) ================================================ FILE: vendor/github.com/codegangsta/cli/help.go ================================================ package cli import ( "fmt" "io" "os" "strings" "text/tabwriter" "text/template" ) // 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}}{{if .Usage}} - {{.Usage}}{{end}} USAGE: {{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 .Description}} DESCRIPTION: {{.Description}}{{end}}{{if len .Authors}} AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: {{range $index, $author := .Authors}}{{if $index}} {{end}}{{$author}}{{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 $index, $option := .VisibleFlags}}{{if $index}} {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} COPYRIGHT: {{.Copyright}}{{end}} ` // 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: {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: {{.Description}}{{end}}{{if .VisibleFlags}} OPTIONS: {{range .VisibleFlags}}{{.}} {{end}}{{end}} ` // 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}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} USAGE: {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} COMMANDS:{{range .VisibleCategories}}{{if .Name}} {{.Name}}:{{end}}{{range .VisibleCommands}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} {{end}}{{if .VisibleFlags}} OPTIONS: {{range .VisibleFlags}}{{.}} {{end}}{{end}} ` var helpCommand = Command{ Name: "help", Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", Action: func(c *Context) error { args := c.Args() if args.Present() { return ShowCommandHelp(c, args.First()) } ShowAppHelp(c) return nil }, } var helpSubcommand = Command{ Name: "help", Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", Action: func(c *Context) error { args := c.Args() if args.Present() { 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{}) // Prints help for the App or Command with custom template function. type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]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 // HelpPrinterCustom is same as HelpPrinter but // takes a custom function for template function map. var HelpPrinterCustom helpPrinterCustom = printHelpCustom // VersionPrinter prints the version for the App var VersionPrinter = printVersion // ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. func ShowAppHelpAndExit(c *Context, exitCode int) { ShowAppHelp(c) os.Exit(exitCode) } // ShowAppHelp is an action that displays the help. func ShowAppHelp(c *Context) (err error) { if c.App.CustomAppHelpTemplate == "" { HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) return } customAppData := func() map[string]interface{} { if c.App.ExtraInfo == nil { return nil } return map[string]interface{}{ "ExtraInfo": c.App.ExtraInfo, } } HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData()) return nil } // 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) } } } // ShowCommandHelpAndExit - exits with code after showing help func ShowCommandHelpAndExit(c *Context, command string, code int) { ShowCommandHelp(c, command) os.Exit(code) } // 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 nil } for _, c := range ctx.App.Commands { if c.HasName(command) { if c.CustomHelpTemplate != "" { HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil) } else { HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) } return nil } } if ctx.App.CommandNotFound == nil { return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3) } ctx.App.CommandNotFound(ctx, command) return nil } // ShowSubcommandHelp prints help for the given subcommand func ShowSubcommandHelp(c *Context) error { return ShowCommandHelp(c, c.Command.Name) } // ShowVersion prints the version number of the App func ShowVersion(c *Context) { VersionPrinter(c) } func printVersion(c *Context) { fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) } // ShowCompletions prints the lists of commands within a given context func ShowCompletions(c *Context) { a := c.App if a != nil && a.BashComplete != nil { a.BashComplete(c) } } // 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 { c.BashComplete(ctx) } } func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { funcMap := template.FuncMap{ "join": strings.Join, } if customFunc != nil { for key, value := range customFunc { funcMap[key] = value } } 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. if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) } return } w.Flush() } func printHelp(out io.Writer, templ string, data interface{}) { printHelpCustom(out, templ, data, nil) } func checkVersion(c *Context) bool { found := false if VersionFlag.GetName() != "" { eachName(VersionFlag.GetName(), func(name string) { if c.GlobalBool(name) || c.Bool(name) { found = true } }) } return found } func checkHelp(c *Context) bool { found := false if HelpFlag.GetName() != "" { eachName(HelpFlag.GetName(), func(name string) { if c.GlobalBool(name) || c.Bool(name) { found = true } }) } return found } func checkCommandHelp(c *Context, name string) bool { if c.Bool("h") || c.Bool("help") { ShowCommandHelp(c, name) return true } return false } func checkSubcommandHelp(c *Context) bool { if c.Bool("h") || c.Bool("help") { ShowSubcommandHelp(c) return true } return false } func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { if !a.EnableBashCompletion { return false, arguments } pos := len(arguments) - 1 lastArg := arguments[pos] if lastArg != "--"+BashCompletionFlag.GetName() { return false, arguments } return true, arguments[:pos] } func checkCompletions(c *Context) bool { if !c.shellComplete { return false } if args := c.Args(); args.Present() { name := args.First() if cmd := c.App.Command(name); cmd != nil { // let the command handle the completion return false } } ShowCompletions(c) return true } func checkCommandCompletions(c *Context, name string) bool { if !c.shellComplete { return false } ShowCommandCompletions(c, name) return true } ================================================ FILE: vendor/github.com/codegangsta/cli/help_test.go ================================================ package cli import ( "bytes" "flag" "fmt" "runtime" "strings" "testing" ) func Test_ShowAppHelp_NoAuthor(t *testing.T) { output := new(bytes.Buffer) app := NewApp() app.Writer = output c := NewContext(app, nil, nil) ShowAppHelp(c) if bytes.Index(output.Bytes(), []byte("AUTHOR(S):")) != -1 { t.Errorf("expected\n%snot to include %s", output.String(), "AUTHOR(S):") } } func Test_ShowAppHelp_NoVersion(t *testing.T) { output := new(bytes.Buffer) app := NewApp() app.Writer = output app.Version = "" c := NewContext(app, nil, nil) ShowAppHelp(c) if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 { t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:") } } func Test_ShowAppHelp_HideVersion(t *testing.T) { output := new(bytes.Buffer) app := NewApp() app.Writer = output app.HideVersion = true c := NewContext(app, nil, nil) ShowAppHelp(c) if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 { t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:") } } func Test_Help_Custom_Flags(t *testing.T) { oldFlag := HelpFlag defer func() { HelpFlag = oldFlag }() HelpFlag = BoolFlag{ Name: "help, x", Usage: "show help", } app := App{ Flags: []Flag{ BoolFlag{Name: "foo, h"}, }, Action: func(ctx *Context) error { if ctx.Bool("h") != true { t.Errorf("custom help flag not set") } return nil }, } output := new(bytes.Buffer) app.Writer = output app.Run([]string{"test", "-h"}) if output.Len() > 0 { t.Errorf("unexpected output: %s", output.String()) } } func Test_Version_Custom_Flags(t *testing.T) { oldFlag := VersionFlag defer func() { VersionFlag = oldFlag }() VersionFlag = BoolFlag{ Name: "version, V", Usage: "show version", } app := App{ Flags: []Flag{ BoolFlag{Name: "foo, v"}, }, Action: func(ctx *Context) error { if ctx.Bool("v") != true { t.Errorf("custom version flag not set") } return nil }, } output := new(bytes.Buffer) app.Writer = output app.Run([]string{"test", "-v"}) if output.Len() > 0 { 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 TestShowCommandHelp_Customtemplate(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", Action: func(ctx *Context) error { return nil }, HelpName: "foo frobbly", CustomHelpTemplate: `NAME: {{.HelpName}} - {{.Usage}} USAGE: {{.HelpName}} [FLAGS] TARGET [TARGET ...] FLAGS: {{range .VisibleFlags}}{{.}} {{end}} EXAMPLES: 1. Frobbly runs with this param locally. $ {{.HelpName}} wobbly `, }, }, } output := &bytes.Buffer{} app.Writer = output app.Run([]string{"foo", "help", "frobbly"}) if strings.Contains(output.String(), "2. Frobbly runs without this param locally.") { t.Errorf("expected output to exclude \"2. Frobbly runs without this param locally.\"; got: %q", output.String()) } if !strings.Contains(output.String(), "1. Frobbly runs with this param locally.") { t.Errorf("expected output to include \"1. Frobbly runs with this param locally.\"; got: %q", output.String()) } if !strings.Contains(output.String(), "$ foo frobbly wobbly") { t.Errorf("expected output to include \"$ foo frobbly wobbly\"; got: %q", output.String()) } } func TestShowSubcommandHelp_CommandUsageText(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", UsageText: "this is usage text", }, }, } output := &bytes.Buffer{} app.Writer = output app.Run([]string{"foo", "frobbly", "--help"}) if !strings.Contains(output.String(), "this is usage text") { t.Errorf("expected output to include usage text; got: %q", output.String()) } } func TestShowSubcommandHelp_SubcommandUsageText(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", Subcommands: []Command{ { Name: "bobbly", UsageText: "this is usage text", }, }, }, }, } output := &bytes.Buffer{} app.Writer = output app.Run([]string{"foo", "frobbly", "bobbly", "--help"}) if !strings.Contains(output.String(), "this is usage text") { t.Errorf("expected output to include usage text; 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()) } } func TestShowAppHelp_CustomAppTemplate(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 }, }, }, ExtraInfo: func() map[string]string { platform := fmt.Sprintf("OS: %s | Arch: %s", runtime.GOOS, runtime.GOARCH) goruntime := fmt.Sprintf("Version: %s | CPUs: %d", runtime.Version(), runtime.NumCPU()) return map[string]string{ "PLATFORM": platform, "RUNTIME": goruntime, } }, CustomAppHelpTemplate: `NAME: {{.Name}} - {{.Usage}} USAGE: {{.Name}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}} [COMMAND FLAGS | -h]{{end}} [ARGUMENTS...] COMMANDS: {{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} {{end}}{{if .VisibleFlags}} GLOBAL FLAGS: {{range .VisibleFlags}}{{.}} {{end}}{{end}} VERSION: 2.0.0 {{"\n"}}{{range $key, $value := ExtraInfo}} {{$key}}: {{$value}} {{end}}`, } 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()) } if !strings.Contains(output.String(), "PLATFORM:") || !strings.Contains(output.String(), "OS:") || !strings.Contains(output.String(), "Arch:") { t.Errorf("expected output to include \"PLATFORM:, OS: and Arch:\"; got: %q", output.String()) } if !strings.Contains(output.String(), "RUNTIME:") || !strings.Contains(output.String(), "Version:") || !strings.Contains(output.String(), "CPUs:") { t.Errorf("expected output to include \"RUNTIME:, Version: and CPUs:\"; got: %q", output.String()) } if !strings.Contains(output.String(), "VERSION:") || !strings.Contains(output.String(), "2.0.0") { t.Errorf("expected output to include \"VERSION:, 2.0.0\"; got: %q", output.String()) } } ================================================ FILE: vendor/github.com/codegangsta/cli/helpers_test.go ================================================ package cli import ( "os" "reflect" "runtime" "strings" "testing" ) 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("(%s:%d) Expected %v (type %v) - Got %v (type %v)", fn, line, b, reflect.TypeOf(b), a, reflect.TypeOf(a)) } } func refute(t *testing.T, a interface{}, b interface{}) { if reflect.DeepEqual(a, b) { t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) } } ================================================ FILE: vendor/github.com/codegangsta/cli/helpers_unix_test.go ================================================ // +build darwin dragonfly freebsd linux netbsd openbsd solaris package cli import "os" func clearenv() { os.Clearenv() } ================================================ FILE: vendor/github.com/codegangsta/cli/helpers_windows_test.go ================================================ package cli import ( "os" "syscall" ) // os.Clearenv() doesn't actually unset variables on Windows // See: https://github.com/golang/go/issues/17902 func clearenv() { for _, s := range os.Environ() { for j := 1; j < len(s); j++ { if s[j] == '=' { keyp, _ := syscall.UTF16PtrFromString(s[0:j]) syscall.SetEnvironmentVariable(keyp, nil) break } } } } ================================================ FILE: vendor/github.com/codegangsta/cli/runtests ================================================ #!/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, 'gfmrun': _gfmrun, 'toc': _toc, 'gen': _gen, } 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 .') 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)) os.remove(combined_name) def _gfmrun(): go_version = check_output('go version'.split()).split()[2] if go_version < 'go1.3': print('runtests: skip on {}'.format(go_version), file=sys.stderr) return _run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) def _vet(): _run('go vet ./...') def _toc(): _run('node_modules/.bin/markdown-toc -i README.md') _run('git diff --exit-code') def _gen(): go_version = check_output('go version'.split()).split()[2] if go_version < 'go1.5': print('runtests: skip on {}'.format(go_version), file=sys.stderr) return _run('go generate ./...') _run('git diff --exit-code') def _run(command): if hasattr(command, 'split'): command = command.split() print('runtests: {}'.format(' '.join(command)), file=sys.stderr) check_call(command) def _gfmrun_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()) ================================================ FILE: vendor/github.com/mitchellh/go-homedir/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013 Mitchell Hashimoto 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. ================================================ FILE: vendor/github.com/mitchellh/go-homedir/README.md ================================================ # go-homedir This is a Go library for detecting the user's home directory without the use of cgo, so the library can be used in cross-compilation environments. Usage is incredibly simple, just call `homedir.Dir()` to get the home directory for a user, and `homedir.Expand()` to expand the `~` in a path to the home directory. **Why not just use `os/user`?** The built-in `os/user` package requires cgo on Darwin systems. This means that any Go code that uses that package cannot cross compile. But 99% of the time the use for `os/user` is just to retrieve the home directory, which we can do for the current user without cgo. This library does that, enabling cross-compilation. ================================================ FILE: vendor/github.com/mitchellh/go-homedir/homedir.go ================================================ package homedir import ( "bytes" "errors" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "sync" ) // DisableCache will disable caching of the home directory. Caching is enabled // by default. var DisableCache bool var homedirCache string var cacheLock sync.RWMutex // Dir returns the home directory for the executing user. // // This uses an OS-specific method for discovering the home directory. // An error is returned if a home directory cannot be detected. func Dir() (string, error) { if !DisableCache { cacheLock.RLock() cached := homedirCache cacheLock.RUnlock() if cached != "" { return cached, nil } } cacheLock.Lock() defer cacheLock.Unlock() var result string var err error if runtime.GOOS == "windows" { result, err = dirWindows() } else { // Unix-like system, so just assume Unix result, err = dirUnix() } if err != nil { return "", err } homedirCache = result return result, nil } // Expand expands the path to include the home directory if the path // is prefixed with `~`. If it isn't prefixed with `~`, the path is // returned as-is. func Expand(path string) (string, error) { if len(path) == 0 { return path, nil } if path[0] != '~' { return path, nil } if len(path) > 1 && path[1] != '/' && path[1] != '\\' { return "", errors.New("cannot expand user-specific home dir") } dir, err := Dir() if err != nil { return "", err } return filepath.Join(dir, path[1:]), nil } func dirUnix() (string, error) { // First prefer the HOME environmental variable if home := os.Getenv("HOME"); home != "" { return home, nil } // If that fails, try getent var stdout bytes.Buffer cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) cmd.Stdout = &stdout if err := cmd.Run(); err != nil { // If the error is ErrNotFound, we ignore it. Otherwise, return it. if err != exec.ErrNotFound { return "", err } } else { if passwd := strings.TrimSpace(stdout.String()); passwd != "" { // username:password:uid:gid:gecos:home:shell passwdParts := strings.SplitN(passwd, ":", 7) if len(passwdParts) > 5 { return passwdParts[5], nil } } } // If all else fails, try the shell stdout.Reset() cmd = exec.Command("sh", "-c", "cd && pwd") cmd.Stdout = &stdout if err := cmd.Run(); err != nil { return "", err } result := strings.TrimSpace(stdout.String()) if result == "" { return "", errors.New("blank output when reading home directory") } return result, nil } func dirWindows() (string, error) { // First prefer the HOME environmental variable if home := os.Getenv("HOME"); home != "" { return home, nil } drive := os.Getenv("HOMEDRIVE") path := os.Getenv("HOMEPATH") home := drive + path if drive == "" || path == "" { home = os.Getenv("USERPROFILE") } if home == "" { return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank") } return home, nil } ================================================ FILE: vendor/github.com/mitchellh/go-homedir/homedir_test.go ================================================ package homedir import ( "os" "os/user" "path/filepath" "testing" ) func patchEnv(key, value string) func() { bck := os.Getenv(key) deferFunc := func() { os.Setenv(key, bck) } os.Setenv(key, value) return deferFunc } func BenchmarkDir(b *testing.B) { // We do this for any "warmups" for i := 0; i < 10; i++ { Dir() } b.ResetTimer() for i := 0; i < b.N; i++ { Dir() } } func TestDir(t *testing.T) { u, err := user.Current() if err != nil { t.Fatalf("err: %s", err) } dir, err := Dir() if err != nil { t.Fatalf("err: %s", err) } if u.HomeDir != dir { t.Fatalf("%#v != %#v", u.HomeDir, dir) } } func TestExpand(t *testing.T) { u, err := user.Current() if err != nil { t.Fatalf("err: %s", err) } cases := []struct { Input string Output string Err bool }{ { "/foo", "/foo", false, }, { "~/foo", filepath.Join(u.HomeDir, "foo"), false, }, { "", "", false, }, { "~", u.HomeDir, false, }, { "~foo/foo", "", true, }, } for _, tc := range cases { actual, err := Expand(tc.Input) if (err != nil) != tc.Err { t.Fatalf("Input: %#v\n\nErr: %s", tc.Input, err) } if actual != tc.Output { t.Fatalf("Input: %#v\n\nOutput: %#v", tc.Input, actual) } } DisableCache = true defer func() { DisableCache = false }() defer patchEnv("HOME", "/custom/path/")() expected := filepath.Join("/", "custom", "path", "foo/bar") actual, err := Expand("~/foo/bar") if err != nil { t.Errorf("No error is expected, got: %v", err) } else if actual != expected { t.Errorf("Expected: %v; actual: %v", expected, actual) } } ================================================ FILE: vendor/gopkg.in/yaml.v2/.travis.yml ================================================ language: go go: - 1.4 - 1.5 - 1.6 - tip go_import_path: gopkg.in/yaml.v2 ================================================ FILE: vendor/gopkg.in/yaml.v2/LICENSE ================================================ Copyright 2011-2016 Canonical Ltd. 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 http://www.apache.org/licenses/LICENSE-2.0 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. ================================================ FILE: vendor/gopkg.in/yaml.v2/LICENSE.libyaml ================================================ The following files were ported to Go from C files of libyaml, and thus are still covered by their original copyright and license: apic.go emitterc.go parserc.go readerc.go scannerc.go writerc.go yamlh.go yamlprivateh.go Copyright (c) 2006 Kirill Simonov 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. ================================================ FILE: vendor/gopkg.in/yaml.v2/README.md ================================================ # YAML support for the Go language Introduction ------------ The yaml package enables Go programs to comfortably encode and decode YAML values. It was developed within [Canonical](https://www.canonical.com) as part of the [juju](https://juju.ubuntu.com) project, and is based on a pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) C library to parse and generate YAML data quickly and reliably. Compatibility ------------- The yaml package supports most of YAML 1.1 and 1.2, including support for anchors, tags, map merging, etc. Multi-document unmarshalling is not yet implemented, and base-60 floats from YAML 1.1 are purposefully not supported since they're a poor design and are gone in YAML 1.2. Installation and usage ---------------------- The import path for the package is *gopkg.in/yaml.v2*. To install it, run: go get gopkg.in/yaml.v2 API documentation ----------------- If opened in a browser, the import path itself leads to the API documentation: * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) API stability ------------- The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). License ------- The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details. Example ------- Some more examples can be found in the "examples" folder. ```Go package main import ( "fmt" "log" "gopkg.in/yaml.v2" ) var data = ` a: Easy! b: c: 2 d: [3, 4] ` type T struct { A string B struct { RenamedC int `yaml:"c"` D []int `yaml:",flow"` } } func main() { t := T{} err := yaml.Unmarshal([]byte(data), &t) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- t:\n%v\n\n", t) d, err := yaml.Marshal(&t) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- t dump:\n%s\n\n", string(d)) m := make(map[interface{}]interface{}) err = yaml.Unmarshal([]byte(data), &m) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- m:\n%v\n\n", m) d, err = yaml.Marshal(&m) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- m dump:\n%s\n\n", string(d)) } ``` This example will generate the following output: ``` --- t: {Easy! {2 [3 4]}} --- t dump: a: Easy! b: c: 2 d: [3, 4] --- m: map[a:Easy! b:map[c:2 d:[3 4]]] --- m dump: a: Easy! b: c: 2 d: - 3 - 4 ``` ================================================ FILE: vendor/gopkg.in/yaml.v2/apic.go ================================================ package yaml import ( "io" "os" ) func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) // Check if we can move the queue at the beginning of the buffer. if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { if parser.tokens_head != len(parser.tokens) { copy(parser.tokens, parser.tokens[parser.tokens_head:]) } parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] parser.tokens_head = 0 } parser.tokens = append(parser.tokens, *token) if pos < 0 { return } copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) parser.tokens[parser.tokens_head+pos] = *token } // Create a new parser object. func yaml_parser_initialize(parser *yaml_parser_t) bool { *parser = yaml_parser_t{ raw_buffer: make([]byte, 0, input_raw_buffer_size), buffer: make([]byte, 0, input_buffer_size), } return true } // Destroy a parser object. func yaml_parser_delete(parser *yaml_parser_t) { *parser = yaml_parser_t{} } // String read handler. func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { if parser.input_pos == len(parser.input) { return 0, io.EOF } n = copy(buffer, parser.input[parser.input_pos:]) parser.input_pos += n return n, nil } // File read handler. func yaml_file_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { return parser.input_file.Read(buffer) } // Set a string input. func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { if parser.read_handler != nil { panic("must set the input source only once") } parser.read_handler = yaml_string_read_handler parser.input = input parser.input_pos = 0 } // Set a file input. func yaml_parser_set_input_file(parser *yaml_parser_t, file *os.File) { if parser.read_handler != nil { panic("must set the input source only once") } parser.read_handler = yaml_file_read_handler parser.input_file = file } // Set the source encoding. func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { if parser.encoding != yaml_ANY_ENCODING { panic("must set the encoding only once") } parser.encoding = encoding } // Create a new emitter object. func yaml_emitter_initialize(emitter *yaml_emitter_t) bool { *emitter = yaml_emitter_t{ buffer: make([]byte, output_buffer_size), raw_buffer: make([]byte, 0, output_raw_buffer_size), states: make([]yaml_emitter_state_t, 0, initial_stack_size), events: make([]yaml_event_t, 0, initial_queue_size), } return true } // Destroy an emitter object. func yaml_emitter_delete(emitter *yaml_emitter_t) { *emitter = yaml_emitter_t{} } // String write handler. func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { *emitter.output_buffer = append(*emitter.output_buffer, buffer...) return nil } // File write handler. func yaml_file_write_handler(emitter *yaml_emitter_t, buffer []byte) error { _, err := emitter.output_file.Write(buffer) return err } // Set a string output. func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { if emitter.write_handler != nil { panic("must set the output target only once") } emitter.write_handler = yaml_string_write_handler emitter.output_buffer = output_buffer } // Set a file output. func yaml_emitter_set_output_file(emitter *yaml_emitter_t, file io.Writer) { if emitter.write_handler != nil { panic("must set the output target only once") } emitter.write_handler = yaml_file_write_handler emitter.output_file = file } // Set the output encoding. func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { if emitter.encoding != yaml_ANY_ENCODING { panic("must set the output encoding only once") } emitter.encoding = encoding } // Set the canonical output style. func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { emitter.canonical = canonical } //// Set the indentation increment. func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { if indent < 2 || indent > 9 { indent = 2 } emitter.best_indent = indent } // Set the preferred line width. func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { if width < 0 { width = -1 } emitter.best_width = width } // Set if unescaped non-ASCII characters are allowed. func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { emitter.unicode = unicode } // Set the preferred line break character. func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { emitter.line_break = line_break } ///* // * Destroy a token object. // */ // //YAML_DECLARE(void) //yaml_token_delete(yaml_token_t *token) //{ // assert(token); // Non-NULL token object expected. // // switch (token.type) // { // case YAML_TAG_DIRECTIVE_TOKEN: // yaml_free(token.data.tag_directive.handle); // yaml_free(token.data.tag_directive.prefix); // break; // // case YAML_ALIAS_TOKEN: // yaml_free(token.data.alias.value); // break; // // case YAML_ANCHOR_TOKEN: // yaml_free(token.data.anchor.value); // break; // // case YAML_TAG_TOKEN: // yaml_free(token.data.tag.handle); // yaml_free(token.data.tag.suffix); // break; // // case YAML_SCALAR_TOKEN: // yaml_free(token.data.scalar.value); // break; // // default: // break; // } // // memset(token, 0, sizeof(yaml_token_t)); //} // ///* // * Check if a string is a valid UTF-8 sequence. // * // * Check 'reader.c' for more details on UTF-8 encoding. // */ // //static int //yaml_check_utf8(yaml_char_t *start, size_t length) //{ // yaml_char_t *end = start+length; // yaml_char_t *pointer = start; // // while (pointer < end) { // unsigned char octet; // unsigned int width; // unsigned int value; // size_t k; // // octet = pointer[0]; // width = (octet & 0x80) == 0x00 ? 1 : // (octet & 0xE0) == 0xC0 ? 2 : // (octet & 0xF0) == 0xE0 ? 3 : // (octet & 0xF8) == 0xF0 ? 4 : 0; // value = (octet & 0x80) == 0x00 ? octet & 0x7F : // (octet & 0xE0) == 0xC0 ? octet & 0x1F : // (octet & 0xF0) == 0xE0 ? octet & 0x0F : // (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; // if (!width) return 0; // if (pointer+width > end) return 0; // for (k = 1; k < width; k ++) { // octet = pointer[k]; // if ((octet & 0xC0) != 0x80) return 0; // value = (value << 6) + (octet & 0x3F); // } // if (!((width == 1) || // (width == 2 && value >= 0x80) || // (width == 3 && value >= 0x800) || // (width == 4 && value >= 0x10000))) return 0; // // pointer += width; // } // // return 1; //} // // Create STREAM-START. func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) bool { *event = yaml_event_t{ typ: yaml_STREAM_START_EVENT, encoding: encoding, } return true } // Create STREAM-END. func yaml_stream_end_event_initialize(event *yaml_event_t) bool { *event = yaml_event_t{ typ: yaml_STREAM_END_EVENT, } return true } // Create DOCUMENT-START. func yaml_document_start_event_initialize(event *yaml_event_t, version_directive *yaml_version_directive_t, tag_directives []yaml_tag_directive_t, implicit bool) bool { *event = yaml_event_t{ typ: yaml_DOCUMENT_START_EVENT, version_directive: version_directive, tag_directives: tag_directives, implicit: implicit, } return true } // Create DOCUMENT-END. func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) bool { *event = yaml_event_t{ typ: yaml_DOCUMENT_END_EVENT, implicit: implicit, } return true } ///* // * Create ALIAS. // */ // //YAML_DECLARE(int) //yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t) //{ // mark yaml_mark_t = { 0, 0, 0 } // anchor_copy *yaml_char_t = NULL // // assert(event) // Non-NULL event object is expected. // assert(anchor) // Non-NULL anchor is expected. // // if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0 // // anchor_copy = yaml_strdup(anchor) // if (!anchor_copy) // return 0 // // ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark) // // return 1 //} // Create SCALAR. func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, anchor: anchor, tag: tag, value: value, implicit: plain_implicit, quoted_implicit: quoted_implicit, style: yaml_style_t(style), } return true } // Create SEQUENCE-START. func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(style), } return true } // Create SEQUENCE-END. func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, } return true } // Create MAPPING-START. func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) bool { *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(style), } return true } // Create MAPPING-END. func yaml_mapping_end_event_initialize(event *yaml_event_t) bool { *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, } return true } // Destroy an event object. func yaml_event_delete(event *yaml_event_t) { *event = yaml_event_t{} } ///* // * Create a document object. // */ // //YAML_DECLARE(int) //yaml_document_initialize(document *yaml_document_t, // version_directive *yaml_version_directive_t, // tag_directives_start *yaml_tag_directive_t, // tag_directives_end *yaml_tag_directive_t, // start_implicit int, end_implicit int) //{ // struct { // error yaml_error_type_t // } context // struct { // start *yaml_node_t // end *yaml_node_t // top *yaml_node_t // } nodes = { NULL, NULL, NULL } // version_directive_copy *yaml_version_directive_t = NULL // struct { // start *yaml_tag_directive_t // end *yaml_tag_directive_t // top *yaml_tag_directive_t // } tag_directives_copy = { NULL, NULL, NULL } // value yaml_tag_directive_t = { NULL, NULL } // mark yaml_mark_t = { 0, 0, 0 } // // assert(document) // Non-NULL document object is expected. // assert((tag_directives_start && tag_directives_end) || // (tag_directives_start == tag_directives_end)) // // Valid tag directives are expected. // // if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error // // if (version_directive) { // version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) // if (!version_directive_copy) goto error // version_directive_copy.major = version_directive.major // version_directive_copy.minor = version_directive.minor // } // // if (tag_directives_start != tag_directives_end) { // tag_directive *yaml_tag_directive_t // if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) // goto error // for (tag_directive = tag_directives_start // tag_directive != tag_directives_end; tag_directive ++) { // assert(tag_directive.handle) // assert(tag_directive.prefix) // if (!yaml_check_utf8(tag_directive.handle, // strlen((char *)tag_directive.handle))) // goto error // if (!yaml_check_utf8(tag_directive.prefix, // strlen((char *)tag_directive.prefix))) // goto error // value.handle = yaml_strdup(tag_directive.handle) // value.prefix = yaml_strdup(tag_directive.prefix) // if (!value.handle || !value.prefix) goto error // if (!PUSH(&context, tag_directives_copy, value)) // goto error // value.handle = NULL // value.prefix = NULL // } // } // // DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, // tag_directives_copy.start, tag_directives_copy.top, // start_implicit, end_implicit, mark, mark) // // return 1 // //error: // STACK_DEL(&context, nodes) // yaml_free(version_directive_copy) // while (!STACK_EMPTY(&context, tag_directives_copy)) { // value yaml_tag_directive_t = POP(&context, tag_directives_copy) // yaml_free(value.handle) // yaml_free(value.prefix) // } // STACK_DEL(&context, tag_directives_copy) // yaml_free(value.handle) // yaml_free(value.prefix) // // return 0 //} // ///* // * Destroy a document object. // */ // //YAML_DECLARE(void) //yaml_document_delete(document *yaml_document_t) //{ // struct { // error yaml_error_type_t // } context // tag_directive *yaml_tag_directive_t // // context.error = YAML_NO_ERROR // Eliminate a compliler warning. // // assert(document) // Non-NULL document object is expected. // // while (!STACK_EMPTY(&context, document.nodes)) { // node yaml_node_t = POP(&context, document.nodes) // yaml_free(node.tag) // switch (node.type) { // case YAML_SCALAR_NODE: // yaml_free(node.data.scalar.value) // break // case YAML_SEQUENCE_NODE: // STACK_DEL(&context, node.data.sequence.items) // break // case YAML_MAPPING_NODE: // STACK_DEL(&context, node.data.mapping.pairs) // break // default: // assert(0) // Should not happen. // } // } // STACK_DEL(&context, document.nodes) // // yaml_free(document.version_directive) // for (tag_directive = document.tag_directives.start // tag_directive != document.tag_directives.end // tag_directive++) { // yaml_free(tag_directive.handle) // yaml_free(tag_directive.prefix) // } // yaml_free(document.tag_directives.start) // // memset(document, 0, sizeof(yaml_document_t)) //} // ///** // * Get a document node. // */ // //YAML_DECLARE(yaml_node_t *) //yaml_document_get_node(document *yaml_document_t, index int) //{ // assert(document) // Non-NULL document object is expected. // // if (index > 0 && document.nodes.start + index <= document.nodes.top) { // return document.nodes.start + index - 1 // } // return NULL //} // ///** // * Get the root object. // */ // //YAML_DECLARE(yaml_node_t *) //yaml_document_get_root_node(document *yaml_document_t) //{ // assert(document) // Non-NULL document object is expected. // // if (document.nodes.top != document.nodes.start) { // return document.nodes.start // } // return NULL //} // ///* // * Add a scalar node to a document. // */ // //YAML_DECLARE(int) //yaml_document_add_scalar(document *yaml_document_t, // tag *yaml_char_t, value *yaml_char_t, length int, // style yaml_scalar_style_t) //{ // struct { // error yaml_error_type_t // } context // mark yaml_mark_t = { 0, 0, 0 } // tag_copy *yaml_char_t = NULL // value_copy *yaml_char_t = NULL // node yaml_node_t // // assert(document) // Non-NULL document object is expected. // assert(value) // Non-NULL value is expected. // // if (!tag) { // tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG // } // // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error // tag_copy = yaml_strdup(tag) // if (!tag_copy) goto error // // if (length < 0) { // length = strlen((char *)value) // } // // if (!yaml_check_utf8(value, length)) goto error // value_copy = yaml_malloc(length+1) // if (!value_copy) goto error // memcpy(value_copy, value, length) // value_copy[length] = '\0' // // SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) // if (!PUSH(&context, document.nodes, node)) goto error // // return document.nodes.top - document.nodes.start // //error: // yaml_free(tag_copy) // yaml_free(value_copy) // // return 0 //} // ///* // * Add a sequence node to a document. // */ // //YAML_DECLARE(int) //yaml_document_add_sequence(document *yaml_document_t, // tag *yaml_char_t, style yaml_sequence_style_t) //{ // struct { // error yaml_error_type_t // } context // mark yaml_mark_t = { 0, 0, 0 } // tag_copy *yaml_char_t = NULL // struct { // start *yaml_node_item_t // end *yaml_node_item_t // top *yaml_node_item_t // } items = { NULL, NULL, NULL } // node yaml_node_t // // assert(document) // Non-NULL document object is expected. // // if (!tag) { // tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG // } // // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error // tag_copy = yaml_strdup(tag) // if (!tag_copy) goto error // // if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error // // SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, // style, mark, mark) // if (!PUSH(&context, document.nodes, node)) goto error // // return document.nodes.top - document.nodes.start // //error: // STACK_DEL(&context, items) // yaml_free(tag_copy) // // return 0 //} // ///* // * Add a mapping node to a document. // */ // //YAML_DECLARE(int) //yaml_document_add_mapping(document *yaml_document_t, // tag *yaml_char_t, style yaml_mapping_style_t) //{ // struct { // error yaml_error_type_t // } context // mark yaml_mark_t = { 0, 0, 0 } // tag_copy *yaml_char_t = NULL // struct { // start *yaml_node_pair_t // end *yaml_node_pair_t // top *yaml_node_pair_t // } pairs = { NULL, NULL, NULL } // node yaml_node_t // // assert(document) // Non-NULL document object is expected. // // if (!tag) { // tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG // } // // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error // tag_copy = yaml_strdup(tag) // if (!tag_copy) goto error // // if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error // // MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, // style, mark, mark) // if (!PUSH(&context, document.nodes, node)) goto error // // return document.nodes.top - document.nodes.start // //error: // STACK_DEL(&context, pairs) // yaml_free(tag_copy) // // return 0 //} // ///* // * Append an item to a sequence node. // */ // //YAML_DECLARE(int) //yaml_document_append_sequence_item(document *yaml_document_t, // sequence int, item int) //{ // struct { // error yaml_error_type_t // } context // // assert(document) // Non-NULL document is required. // assert(sequence > 0 // && document.nodes.start + sequence <= document.nodes.top) // // Valid sequence id is required. // assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) // // A sequence node is required. // assert(item > 0 && document.nodes.start + item <= document.nodes.top) // // Valid item id is required. // // if (!PUSH(&context, // document.nodes.start[sequence-1].data.sequence.items, item)) // return 0 // // return 1 //} // ///* // * Append a pair of a key and a value to a mapping node. // */ // //YAML_DECLARE(int) //yaml_document_append_mapping_pair(document *yaml_document_t, // mapping int, key int, value int) //{ // struct { // error yaml_error_type_t // } context // // pair yaml_node_pair_t // // assert(document) // Non-NULL document is required. // assert(mapping > 0 // && document.nodes.start + mapping <= document.nodes.top) // // Valid mapping id is required. // assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) // // A mapping node is required. // assert(key > 0 && document.nodes.start + key <= document.nodes.top) // // Valid key id is required. // assert(value > 0 && document.nodes.start + value <= document.nodes.top) // // Valid value id is required. // // pair.key = key // pair.value = value // // if (!PUSH(&context, // document.nodes.start[mapping-1].data.mapping.pairs, pair)) // return 0 // // return 1 //} // // ================================================ FILE: vendor/gopkg.in/yaml.v2/decode.go ================================================ package yaml import ( "encoding" "encoding/base64" "fmt" "math" "reflect" "strconv" "time" ) const ( documentNode = 1 << iota mappingNode sequenceNode scalarNode aliasNode ) type node struct { kind int line, column int tag string value string implicit bool children []*node anchors map[string]*node } // ---------------------------------------------------------------------------- // Parser, produces a node tree out of a libyaml event stream. type parser struct { parser yaml_parser_t event yaml_event_t doc *node } func newParser(b []byte) *parser { p := parser{} if !yaml_parser_initialize(&p.parser) { panic("failed to initialize YAML emitter") } if len(b) == 0 { b = []byte{'\n'} } yaml_parser_set_input_string(&p.parser, b) p.skip() if p.event.typ != yaml_STREAM_START_EVENT { panic("expected stream start event, got " + strconv.Itoa(int(p.event.typ))) } p.skip() return &p } func (p *parser) destroy() { if p.event.typ != yaml_NO_EVENT { yaml_event_delete(&p.event) } yaml_parser_delete(&p.parser) } func (p *parser) skip() { if p.event.typ != yaml_NO_EVENT { if p.event.typ == yaml_STREAM_END_EVENT { failf("attempted to go past the end of stream; corrupted value?") } yaml_event_delete(&p.event) } if !yaml_parser_parse(&p.parser, &p.event) { p.fail() } } func (p *parser) fail() { var where string var line int if p.parser.problem_mark.line != 0 { line = p.parser.problem_mark.line } else if p.parser.context_mark.line != 0 { line = p.parser.context_mark.line } if line != 0 { where = "line " + strconv.Itoa(line) + ": " } var msg string if len(p.parser.problem) > 0 { msg = p.parser.problem } else { msg = "unknown problem parsing YAML content" } failf("%s%s", where, msg) } func (p *parser) anchor(n *node, anchor []byte) { if anchor != nil { p.doc.anchors[string(anchor)] = n } } func (p *parser) parse() *node { switch p.event.typ { case yaml_SCALAR_EVENT: return p.scalar() case yaml_ALIAS_EVENT: return p.alias() case yaml_MAPPING_START_EVENT: return p.mapping() case yaml_SEQUENCE_START_EVENT: return p.sequence() case yaml_DOCUMENT_START_EVENT: return p.document() case yaml_STREAM_END_EVENT: // Happens when attempting to decode an empty buffer. return nil default: panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ))) } } func (p *parser) node(kind int) *node { return &node{ kind: kind, line: p.event.start_mark.line, column: p.event.start_mark.column, } } func (p *parser) document() *node { n := p.node(documentNode) n.anchors = make(map[string]*node) p.doc = n p.skip() n.children = append(n.children, p.parse()) if p.event.typ != yaml_DOCUMENT_END_EVENT { panic("expected end of document event but got " + strconv.Itoa(int(p.event.typ))) } p.skip() return n } func (p *parser) alias() *node { n := p.node(aliasNode) n.value = string(p.event.anchor) p.skip() return n } func (p *parser) scalar() *node { n := p.node(scalarNode) n.value = string(p.event.value) n.tag = string(p.event.tag) n.implicit = p.event.implicit p.anchor(n, p.event.anchor) p.skip() return n } func (p *parser) sequence() *node { n := p.node(sequenceNode) p.anchor(n, p.event.anchor) p.skip() for p.event.typ != yaml_SEQUENCE_END_EVENT { n.children = append(n.children, p.parse()) } p.skip() return n } func (p *parser) mapping() *node { n := p.node(mappingNode) p.anchor(n, p.event.anchor) p.skip() for p.event.typ != yaml_MAPPING_END_EVENT { n.children = append(n.children, p.parse(), p.parse()) } p.skip() return n } // ---------------------------------------------------------------------------- // Decoder, unmarshals a node into a provided value. type decoder struct { doc *node aliases map[string]bool mapType reflect.Type terrors []string strict bool } var ( mapItemType = reflect.TypeOf(MapItem{}) durationType = reflect.TypeOf(time.Duration(0)) defaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) ifaceType = defaultMapType.Elem() ) func newDecoder(strict bool) *decoder { d := &decoder{mapType: defaultMapType, strict: strict} d.aliases = make(map[string]bool) return d } func (d *decoder) terror(n *node, tag string, out reflect.Value) { if n.tag != "" { tag = n.tag } value := n.value if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG { if len(value) > 10 { value = " `" + value[:7] + "...`" } else { value = " `" + value + "`" } } d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type())) } func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { terrlen := len(d.terrors) err := u.UnmarshalYAML(func(v interface{}) (err error) { defer handleErr(&err) d.unmarshal(n, reflect.ValueOf(v)) if len(d.terrors) > terrlen { issues := d.terrors[terrlen:] d.terrors = d.terrors[:terrlen] return &TypeError{issues} } return nil }) if e, ok := err.(*TypeError); ok { d.terrors = append(d.terrors, e.Errors...) return false } if err != nil { fail(err) } return true } // d.prepare initializes and dereferences pointers and calls UnmarshalYAML // if a value is found to implement it. // It returns the initialized and dereferenced out value, whether // unmarshalling was already done by UnmarshalYAML, and if so whether // its types unmarshalled appropriately. // // If n holds a null value, prepare returns before doing anything. func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "" && n.implicit) { return out, false, false } again := true for again { again = false if out.Kind() == reflect.Ptr { if out.IsNil() { out.Set(reflect.New(out.Type().Elem())) } out = out.Elem() again = true } if out.CanAddr() { if u, ok := out.Addr().Interface().(Unmarshaler); ok { good = d.callUnmarshaler(n, u) return out, true, good } } } return out, false, false } func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { switch n.kind { case documentNode: return d.document(n, out) case aliasNode: return d.alias(n, out) } out, unmarshaled, good := d.prepare(n, out) if unmarshaled { return good } switch n.kind { case scalarNode: good = d.scalar(n, out) case mappingNode: good = d.mapping(n, out) case sequenceNode: good = d.sequence(n, out) default: panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) } return good } func (d *decoder) document(n *node, out reflect.Value) (good bool) { if len(n.children) == 1 { d.doc = n d.unmarshal(n.children[0], out) return true } return false } func (d *decoder) alias(n *node, out reflect.Value) (good bool) { an, ok := d.doc.anchors[n.value] if !ok { failf("unknown anchor '%s' referenced", n.value) } if d.aliases[n.value] { failf("anchor '%s' value contains itself", n.value) } d.aliases[n.value] = true good = d.unmarshal(an, out) delete(d.aliases, n.value) return good } var zeroValue reflect.Value func resetMap(out reflect.Value) { for _, k := range out.MapKeys() { out.SetMapIndex(k, zeroValue) } } func (d *decoder) scalar(n *node, out reflect.Value) (good bool) { var tag string var resolved interface{} if n.tag == "" && !n.implicit { tag = yaml_STR_TAG resolved = n.value } else { tag, resolved = resolve(n.tag, n.value) if tag == yaml_BINARY_TAG { data, err := base64.StdEncoding.DecodeString(resolved.(string)) if err != nil { failf("!!binary value contains invalid base64 data") } resolved = string(data) } } if resolved == nil { if out.Kind() == reflect.Map && !out.CanAddr() { resetMap(out) } else { out.Set(reflect.Zero(out.Type())) } return true } if s, ok := resolved.(string); ok && out.CanAddr() { if u, ok := out.Addr().Interface().(encoding.TextUnmarshaler); ok { err := u.UnmarshalText([]byte(s)) if err != nil { fail(err) } return true } } switch out.Kind() { case reflect.String: if tag == yaml_BINARY_TAG { out.SetString(resolved.(string)) good = true } else if resolved != nil { out.SetString(n.value) good = true } case reflect.Interface: if resolved == nil { out.Set(reflect.Zero(out.Type())) } else { out.Set(reflect.ValueOf(resolved)) } good = true case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: switch resolved := resolved.(type) { case int: if !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) good = true } case int64: if !out.OverflowInt(resolved) { out.SetInt(resolved) good = true } case uint64: if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) good = true } case float64: if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) good = true } case string: if out.Type() == durationType { d, err := time.ParseDuration(resolved) if err == nil { out.SetInt(int64(d)) good = true } } } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: switch resolved := resolved.(type) { case int: if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { out.SetUint(uint64(resolved)) good = true } case int64: if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { out.SetUint(uint64(resolved)) good = true } case uint64: if !out.OverflowUint(uint64(resolved)) { out.SetUint(uint64(resolved)) good = true } case float64: if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { out.SetUint(uint64(resolved)) good = true } } case reflect.Bool: switch resolved := resolved.(type) { case bool: out.SetBool(resolved) good = true } case reflect.Float32, reflect.Float64: switch resolved := resolved.(type) { case int: out.SetFloat(float64(resolved)) good = true case int64: out.SetFloat(float64(resolved)) good = true case uint64: out.SetFloat(float64(resolved)) good = true case float64: out.SetFloat(resolved) good = true } case reflect.Ptr: if out.Type().Elem() == reflect.TypeOf(resolved) { // TODO DOes this make sense? When is out a Ptr except when decoding a nil value? elem := reflect.New(out.Type().Elem()) elem.Elem().Set(reflect.ValueOf(resolved)) out.Set(elem) good = true } } if !good { d.terror(n, tag, out) } return good } func settableValueOf(i interface{}) reflect.Value { v := reflect.ValueOf(i) sv := reflect.New(v.Type()).Elem() sv.Set(v) return sv } func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { l := len(n.children) var iface reflect.Value switch out.Kind() { case reflect.Slice: out.Set(reflect.MakeSlice(out.Type(), l, l)) case reflect.Interface: // No type hints. Will have to use a generic sequence. iface = out out = settableValueOf(make([]interface{}, l)) default: d.terror(n, yaml_SEQ_TAG, out) return false } et := out.Type().Elem() j := 0 for i := 0; i < l; i++ { e := reflect.New(et).Elem() if ok := d.unmarshal(n.children[i], e); ok { out.Index(j).Set(e) j++ } } out.Set(out.Slice(0, j)) if iface.IsValid() { iface.Set(out) } return true } func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { switch out.Kind() { case reflect.Struct: return d.mappingStruct(n, out) case reflect.Slice: return d.mappingSlice(n, out) case reflect.Map: // okay case reflect.Interface: if d.mapType.Kind() == reflect.Map { iface := out out = reflect.MakeMap(d.mapType) iface.Set(out) } else { slicev := reflect.New(d.mapType).Elem() if !d.mappingSlice(n, slicev) { return false } out.Set(slicev) return true } default: d.terror(n, yaml_MAP_TAG, out) return false } outt := out.Type() kt := outt.Key() et := outt.Elem() mapType := d.mapType if outt.Key() == ifaceType && outt.Elem() == ifaceType { d.mapType = outt } if out.IsNil() { out.Set(reflect.MakeMap(outt)) } l := len(n.children) for i := 0; i < l; i += 2 { if isMerge(n.children[i]) { d.merge(n.children[i+1], out) continue } k := reflect.New(kt).Elem() if d.unmarshal(n.children[i], k) { kkind := k.Kind() if kkind == reflect.Interface { kkind = k.Elem().Kind() } if kkind == reflect.Map || kkind == reflect.Slice { failf("invalid map key: %#v", k.Interface()) } e := reflect.New(et).Elem() if d.unmarshal(n.children[i+1], e) { out.SetMapIndex(k, e) } } } d.mapType = mapType return true } func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { outt := out.Type() if outt.Elem() != mapItemType { d.terror(n, yaml_MAP_TAG, out) return false } mapType := d.mapType d.mapType = outt var slice []MapItem var l = len(n.children) for i := 0; i < l; i += 2 { if isMerge(n.children[i]) { d.merge(n.children[i+1], out) continue } item := MapItem{} k := reflect.ValueOf(&item.Key).Elem() if d.unmarshal(n.children[i], k) { v := reflect.ValueOf(&item.Value).Elem() if d.unmarshal(n.children[i+1], v) { slice = append(slice, item) } } } out.Set(reflect.ValueOf(slice)) d.mapType = mapType return true } func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { sinfo, err := getStructInfo(out.Type()) if err != nil { panic(err) } name := settableValueOf("") l := len(n.children) var inlineMap reflect.Value var elemType reflect.Type if sinfo.InlineMap != -1 { inlineMap = out.Field(sinfo.InlineMap) inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) elemType = inlineMap.Type().Elem() } for i := 0; i < l; i += 2 { ni := n.children[i] if isMerge(ni) { d.merge(n.children[i+1], out) continue } if !d.unmarshal(ni, name) { continue } if info, ok := sinfo.FieldsMap[name.String()]; ok { var field reflect.Value if info.Inline == nil { field = out.Field(info.Num) } else { field = out.FieldByIndex(info.Inline) } d.unmarshal(n.children[i+1], field) } else if sinfo.InlineMap != -1 { if inlineMap.IsNil() { inlineMap.Set(reflect.MakeMap(inlineMap.Type())) } value := reflect.New(elemType).Elem() d.unmarshal(n.children[i+1], value) inlineMap.SetMapIndex(name, value) } else if d.strict { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in struct %s", n.line+1, name.String(), out.Type())) } } return true } func failWantMap() { failf("map merge requires map or sequence of maps as the value") } func (d *decoder) merge(n *node, out reflect.Value) { switch n.kind { case mappingNode: d.unmarshal(n, out) case aliasNode: an, ok := d.doc.anchors[n.value] if ok && an.kind != mappingNode { failWantMap() } d.unmarshal(n, out) case sequenceNode: // Step backwards as earlier nodes take precedence. for i := len(n.children) - 1; i >= 0; i-- { ni := n.children[i] if ni.kind == aliasNode { an, ok := d.doc.anchors[ni.value] if ok && an.kind != mappingNode { failWantMap() } } else if ni.kind != mappingNode { failWantMap() } d.unmarshal(ni, out) } default: failWantMap() } } func isMerge(n *node) bool { return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) } ================================================ FILE: vendor/gopkg.in/yaml.v2/decode_test.go ================================================ package yaml_test import ( "errors" . "gopkg.in/check.v1" "gopkg.in/yaml.v2" "math" "net" "reflect" "strings" "time" ) var unmarshalIntTest = 123 var unmarshalTests = []struct { data string value interface{} }{ { "", &struct{}{}, }, { "{}", &struct{}{}, }, { "v: hi", map[string]string{"v": "hi"}, }, { "v: hi", map[string]interface{}{"v": "hi"}, }, { "v: true", map[string]string{"v": "true"}, }, { "v: true", map[string]interface{}{"v": true}, }, { "v: 10", map[string]interface{}{"v": 10}, }, { "v: 0b10", map[string]interface{}{"v": 2}, }, { "v: 0xA", map[string]interface{}{"v": 10}, }, { "v: 4294967296", map[string]int64{"v": 4294967296}, }, { "v: 0.1", map[string]interface{}{"v": 0.1}, }, { "v: .1", map[string]interface{}{"v": 0.1}, }, { "v: .Inf", map[string]interface{}{"v": math.Inf(+1)}, }, { "v: -.Inf", map[string]interface{}{"v": math.Inf(-1)}, }, { "v: -10", map[string]interface{}{"v": -10}, }, { "v: -.1", map[string]interface{}{"v": -0.1}, }, // Simple values. { "123", &unmarshalIntTest, }, // Floats from spec { "canonical: 6.8523e+5", map[string]interface{}{"canonical": 6.8523e+5}, }, { "expo: 685.230_15e+03", map[string]interface{}{"expo": 685.23015e+03}, }, { "fixed: 685_230.15", map[string]interface{}{"fixed": 685230.15}, }, { "neginf: -.inf", map[string]interface{}{"neginf": math.Inf(-1)}, }, { "fixed: 685_230.15", map[string]float64{"fixed": 685230.15}, }, //{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported //{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails. // Bools from spec { "canonical: y", map[string]interface{}{"canonical": true}, }, { "answer: NO", map[string]interface{}{"answer": false}, }, { "logical: True", map[string]interface{}{"logical": true}, }, { "option: on", map[string]interface{}{"option": true}, }, { "option: on", map[string]bool{"option": true}, }, // Ints from spec { "canonical: 685230", map[string]interface{}{"canonical": 685230}, }, { "decimal: +685_230", map[string]interface{}{"decimal": 685230}, }, { "octal: 02472256", map[string]interface{}{"octal": 685230}, }, { "hexa: 0x_0A_74_AE", map[string]interface{}{"hexa": 685230}, }, { "bin: 0b1010_0111_0100_1010_1110", map[string]interface{}{"bin": 685230}, }, { "bin: -0b101010", map[string]interface{}{"bin": -42}, }, { "decimal: +685_230", map[string]int{"decimal": 685230}, }, //{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported // Nulls from spec { "empty:", map[string]interface{}{"empty": nil}, }, { "canonical: ~", map[string]interface{}{"canonical": nil}, }, { "english: null", map[string]interface{}{"english": nil}, }, { "~: null key", map[interface{}]string{nil: "null key"}, }, { "empty:", map[string]*bool{"empty": nil}, }, // Flow sequence { "seq: [A,B]", map[string]interface{}{"seq": []interface{}{"A", "B"}}, }, { "seq: [A,B,C,]", map[string][]string{"seq": []string{"A", "B", "C"}}, }, { "seq: [A,1,C]", map[string][]string{"seq": []string{"A", "1", "C"}}, }, { "seq: [A,1,C]", map[string][]int{"seq": []int{1}}, }, { "seq: [A,1,C]", map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, }, // Block sequence { "seq:\n - A\n - B", map[string]interface{}{"seq": []interface{}{"A", "B"}}, }, { "seq:\n - A\n - B\n - C", map[string][]string{"seq": []string{"A", "B", "C"}}, }, { "seq:\n - A\n - 1\n - C", map[string][]string{"seq": []string{"A", "1", "C"}}, }, { "seq:\n - A\n - 1\n - C", map[string][]int{"seq": []int{1}}, }, { "seq:\n - A\n - 1\n - C", map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, }, // Literal block scalar { "scalar: | # Comment\n\n literal\n\n \ttext\n\n", map[string]string{"scalar": "\nliteral\n\n\ttext\n"}, }, // Folded block scalar { "scalar: > # Comment\n\n folded\n line\n \n next\n line\n * one\n * two\n\n last\n line\n\n", map[string]string{"scalar": "\nfolded line\nnext line\n * one\n * two\n\nlast line\n"}, }, // Map inside interface with no type hints. { "a: {b: c}", map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, }, // Structs and type conversions. { "hello: world", &struct{ Hello string }{"world"}, }, { "a: {b: c}", &struct{ A struct{ B string } }{struct{ B string }{"c"}}, }, { "a: {b: c}", &struct{ A *struct{ B string } }{&struct{ B string }{"c"}}, }, { "a: {b: c}", &struct{ A map[string]string }{map[string]string{"b": "c"}}, }, { "a: {b: c}", &struct{ A *map[string]string }{&map[string]string{"b": "c"}}, }, { "a:", &struct{ A map[string]string }{}, }, { "a: 1", &struct{ A int }{1}, }, { "a: 1", &struct{ A float64 }{1}, }, { "a: 1.0", &struct{ A int }{1}, }, { "a: 1.0", &struct{ A uint }{1}, }, { "a: [1, 2]", &struct{ A []int }{[]int{1, 2}}, }, { "a: 1", &struct{ B int }{0}, }, { "a: 1", &struct { B int "a" }{1}, }, { "a: y", &struct{ A bool }{true}, }, // Some cross type conversions { "v: 42", map[string]uint{"v": 42}, }, { "v: -42", map[string]uint{}, }, { "v: 4294967296", map[string]uint64{"v": 4294967296}, }, { "v: -4294967296", map[string]uint64{}, }, // int { "int_max: 2147483647", map[string]int{"int_max": math.MaxInt32}, }, { "int_min: -2147483648", map[string]int{"int_min": math.MinInt32}, }, { "int_overflow: 9223372036854775808", // math.MaxInt64 + 1 map[string]int{}, }, // int64 { "int64_max: 9223372036854775807", map[string]int64{"int64_max": math.MaxInt64}, }, { "int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111", map[string]int64{"int64_max_base2": math.MaxInt64}, }, { "int64_min: -9223372036854775808", map[string]int64{"int64_min": math.MinInt64}, }, { "int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111", map[string]int64{"int64_neg_base2": -math.MaxInt64}, }, { "int64_overflow: 9223372036854775808", // math.MaxInt64 + 1 map[string]int64{}, }, // uint { "uint_min: 0", map[string]uint{"uint_min": 0}, }, { "uint_max: 4294967295", map[string]uint{"uint_max": math.MaxUint32}, }, { "uint_underflow: -1", map[string]uint{}, }, // uint64 { "uint64_min: 0", map[string]uint{"uint64_min": 0}, }, { "uint64_max: 18446744073709551615", map[string]uint64{"uint64_max": math.MaxUint64}, }, { "uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111", map[string]uint64{"uint64_max_base2": math.MaxUint64}, }, { "uint64_maxint64: 9223372036854775807", map[string]uint64{"uint64_maxint64": math.MaxInt64}, }, { "uint64_underflow: -1", map[string]uint64{}, }, // float32 { "float32_max: 3.40282346638528859811704183484516925440e+38", map[string]float32{"float32_max": math.MaxFloat32}, }, { "float32_nonzero: 1.401298464324817070923729583289916131280e-45", map[string]float32{"float32_nonzero": math.SmallestNonzeroFloat32}, }, { "float32_maxuint64: 18446744073709551615", map[string]float32{"float32_maxuint64": float32(math.MaxUint64)}, }, { "float32_maxuint64+1: 18446744073709551616", map[string]float32{"float32_maxuint64+1": float32(math.MaxUint64 + 1)}, }, // float64 { "float64_max: 1.797693134862315708145274237317043567981e+308", map[string]float64{"float64_max": math.MaxFloat64}, }, { "float64_nonzero: 4.940656458412465441765687928682213723651e-324", map[string]float64{"float64_nonzero": math.SmallestNonzeroFloat64}, }, { "float64_maxuint64: 18446744073709551615", map[string]float64{"float64_maxuint64": float64(math.MaxUint64)}, }, { "float64_maxuint64+1: 18446744073709551616", map[string]float64{"float64_maxuint64+1": float64(math.MaxUint64 + 1)}, }, // Overflow cases. { "v: 4294967297", map[string]int32{}, }, { "v: 128", map[string]int8{}, }, // Quoted values. { "'1': '\"2\"'", map[interface{}]interface{}{"1": "\"2\""}, }, { "v:\n- A\n- 'B\n\n C'\n", map[string][]string{"v": []string{"A", "B\nC"}}, }, // Explicit tags. { "v: !!float '1.1'", map[string]interface{}{"v": 1.1}, }, { "v: !!null ''", map[string]interface{}{"v": nil}, }, { "%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'", map[string]interface{}{"v": 1}, }, // Non-specific tag (Issue #75) { "v: ! test", map[string]interface{}{"v": "test"}, }, // Anchors and aliases. { "a: &x 1\nb: &y 2\nc: *x\nd: *y\n", &struct{ A, B, C, D int }{1, 2, 1, 2}, }, { "a: &a {c: 1}\nb: *a", &struct { A, B struct { C int } }{struct{ C int }{1}, struct{ C int }{1}}, }, { "a: &a [1, 2]\nb: *a", &struct{ B []int }{[]int{1, 2}}, }, { "b: *a\na: &a {c: 1}", &struct { A, B struct { C int } }{struct{ C int }{1}, struct{ C int }{1}}, }, // Bug #1133337 { "foo: ''", map[string]*string{"foo": new(string)}, }, { "foo: null", map[string]string{"foo": ""}, }, { "foo: null", map[string]interface{}{"foo": nil}, }, // Ignored field { "a: 1\nb: 2\n", &struct { A int B int "-" }{1, 0}, }, // Bug #1191981 { "" + "%YAML 1.1\n" + "--- !!str\n" + `"Generic line break (no glyph)\n\` + "\n" + ` Generic line break (glyphed)\n\` + "\n" + ` Line separator\u2028\` + "\n" + ` Paragraph separator\u2029"` + "\n", "" + "Generic line break (no glyph)\n" + "Generic line break (glyphed)\n" + "Line separator\u2028Paragraph separator\u2029", }, // Struct inlining { "a: 1\nb: 2\nc: 3\n", &struct { A int C inlineB `yaml:",inline"` }{1, inlineB{2, inlineC{3}}}, }, // Map inlining { "a: 1\nb: 2\nc: 3\n", &struct { A int C map[string]int `yaml:",inline"` }{1, map[string]int{"b": 2, "c": 3}}, }, // bug 1243827 { "a: -b_c", map[string]interface{}{"a": "-b_c"}, }, { "a: +b_c", map[string]interface{}{"a": "+b_c"}, }, { "a: 50cent_of_dollar", map[string]interface{}{"a": "50cent_of_dollar"}, }, // Duration { "a: 3s", map[string]time.Duration{"a": 3 * time.Second}, }, // Issue #24. { "a: ", map[string]string{"a": ""}, }, // Base 60 floats are obsolete and unsupported. { "a: 1:1\n", map[string]string{"a": "1:1"}, }, // Binary data. { "a: !!binary gIGC\n", map[string]string{"a": "\x80\x81\x82"}, }, { "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", map[string]string{"a": strings.Repeat("\x90", 54)}, }, { "a: !!binary |\n " + strings.Repeat("A", 70) + "\n ==\n", map[string]string{"a": strings.Repeat("\x00", 52)}, }, // Ordered maps. { "{b: 2, a: 1, d: 4, c: 3, sub: {e: 5}}", &yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}}, }, // Issue #39. { "a:\n b:\n c: d\n", map[string]struct{ B interface{} }{"a": {map[interface{}]interface{}{"c": "d"}}}, }, // Custom map type. { "a: {b: c}", M{"a": M{"b": "c"}}, }, // Support encoding.TextUnmarshaler. { "a: 1.2.3.4\n", map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, }, { "a: 2015-02-24T18:19:39Z\n", map[string]time.Time{"a": time.Unix(1424801979, 0).In(time.UTC)}, }, // Encode empty lists as zero-length slices. { "a: []", &struct{ A []int }{[]int{}}, }, // UTF-16-LE { "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00", M{"ñoño": "very yes"}, }, // UTF-16-LE with surrogate. { "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \x00=\xd8\xd4\xdf\n\x00", M{"ñoño": "very yes 🟔"}, }, // UTF-16-BE { "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n", M{"ñoño": "very yes"}, }, // UTF-16-BE with surrogate. { "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n", M{"ñoño": "very yes 🟔"}, }, // YAML Float regex shouldn't match this { "a: 123456e1\n", M{"a": "123456e1"}, }, { "a: 123456E1\n", M{"a": "123456E1"}, }, } type M map[interface{}]interface{} type inlineB struct { B int inlineC `yaml:",inline"` } type inlineC struct { C int } func (s *S) TestUnmarshal(c *C) { for _, item := range unmarshalTests { t := reflect.ValueOf(item.value).Type() var value interface{} switch t.Kind() { case reflect.Map: value = reflect.MakeMap(t).Interface() case reflect.String: value = reflect.New(t).Interface() case reflect.Ptr: value = reflect.New(t.Elem()).Interface() default: c.Fatalf("missing case for %s", t) } err := yaml.Unmarshal([]byte(item.data), value) if _, ok := err.(*yaml.TypeError); !ok { c.Assert(err, IsNil) } if t.Kind() == reflect.String { c.Assert(*value.(*string), Equals, item.value) } else { c.Assert(value, DeepEquals, item.value) } } } func (s *S) TestUnmarshalNaN(c *C) { value := map[string]interface{}{} err := yaml.Unmarshal([]byte("notanum: .NaN"), &value) c.Assert(err, IsNil) c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true) } var unmarshalErrorTests = []struct { data, error string }{ {"v: !!float 'error'", "yaml: cannot decode !!str `error` as a !!float"}, {"v: [A,", "yaml: line 1: did not find expected node content"}, {"v:\n- [A,", "yaml: line 2: did not find expected node content"}, {"a: *b\n", "yaml: unknown anchor 'b' referenced"}, {"a: &a\n b: *a\n", "yaml: anchor 'a' value contains itself"}, {"value: -", "yaml: block sequence entries are not allowed in this context"}, {"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"}, {"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`}, {"{{.}}", `yaml: invalid map key: map\[interface\ \{\}\]interface \{\}\{".":interface \{\}\(nil\)\}`}, } func (s *S) TestUnmarshalErrors(c *C) { for _, item := range unmarshalErrorTests { var value interface{} err := yaml.Unmarshal([]byte(item.data), &value) c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value)) } } var unmarshalerTests = []struct { data, tag string value interface{} }{ {"_: {hi: there}", "!!map", map[interface{}]interface{}{"hi": "there"}}, {"_: [1,A]", "!!seq", []interface{}{1, "A"}}, {"_: 10", "!!int", 10}, {"_: null", "!!null", nil}, {`_: BAR!`, "!!str", "BAR!"}, {`_: "BAR!"`, "!!str", "BAR!"}, {"_: !!foo 'BAR!'", "!!foo", "BAR!"}, {`_: ""`, "!!str", ""}, } var unmarshalerResult = map[int]error{} type unmarshalerType struct { value interface{} } func (o *unmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error { if err := unmarshal(&o.value); err != nil { return err } if i, ok := o.value.(int); ok { if result, ok := unmarshalerResult[i]; ok { return result } } return nil } type unmarshalerPointer struct { Field *unmarshalerType "_" } type unmarshalerValue struct { Field unmarshalerType "_" } func (s *S) TestUnmarshalerPointerField(c *C) { for _, item := range unmarshalerTests { obj := &unmarshalerPointer{} err := yaml.Unmarshal([]byte(item.data), obj) c.Assert(err, IsNil) if item.value == nil { c.Assert(obj.Field, IsNil) } else { c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) c.Assert(obj.Field.value, DeepEquals, item.value) } } } func (s *S) TestUnmarshalerValueField(c *C) { for _, item := range unmarshalerTests { obj := &unmarshalerValue{} err := yaml.Unmarshal([]byte(item.data), obj) c.Assert(err, IsNil) c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) c.Assert(obj.Field.value, DeepEquals, item.value) } } func (s *S) TestUnmarshalerWholeDocument(c *C) { obj := &unmarshalerType{} err := yaml.Unmarshal([]byte(unmarshalerTests[0].data), obj) c.Assert(err, IsNil) value, ok := obj.value.(map[interface{}]interface{}) c.Assert(ok, Equals, true, Commentf("value: %#v", obj.value)) c.Assert(value["_"], DeepEquals, unmarshalerTests[0].value) } func (s *S) TestUnmarshalerTypeError(c *C) { unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}} unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}} defer func() { delete(unmarshalerResult, 2) delete(unmarshalerResult, 4) }() type T struct { Before int After int M map[string]*unmarshalerType } var v T data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}` err := yaml.Unmarshal([]byte(data), &v) c.Assert(err, ErrorMatches, ""+ "yaml: unmarshal errors:\n"+ " line 1: cannot unmarshal !!str `A` into int\n"+ " foo\n"+ " bar\n"+ " line 1: cannot unmarshal !!str `B` into int") c.Assert(v.M["abc"], NotNil) c.Assert(v.M["def"], IsNil) c.Assert(v.M["ghi"], NotNil) c.Assert(v.M["jkl"], IsNil) c.Assert(v.M["abc"].value, Equals, 1) c.Assert(v.M["ghi"].value, Equals, 3) } type proxyTypeError struct{} func (v *proxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string var a int32 var b int64 if err := unmarshal(&s); err != nil { panic(err) } if s == "a" { if err := unmarshal(&b); err == nil { panic("should have failed") } return unmarshal(&a) } if err := unmarshal(&a); err == nil { panic("should have failed") } return unmarshal(&b) } func (s *S) TestUnmarshalerTypeErrorProxying(c *C) { type T struct { Before int After int M map[string]*proxyTypeError } var v T data := `{before: A, m: {abc: a, def: b}, after: B}` err := yaml.Unmarshal([]byte(data), &v) c.Assert(err, ErrorMatches, ""+ "yaml: unmarshal errors:\n"+ " line 1: cannot unmarshal !!str `A` into int\n"+ " line 1: cannot unmarshal !!str `a` into int32\n"+ " line 1: cannot unmarshal !!str `b` into int64\n"+ " line 1: cannot unmarshal !!str `B` into int") } type failingUnmarshaler struct{} var failingErr = errors.New("failingErr") func (ft *failingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error { return failingErr } func (s *S) TestUnmarshalerError(c *C) { err := yaml.Unmarshal([]byte("a: b"), &failingUnmarshaler{}) c.Assert(err, Equals, failingErr) } type sliceUnmarshaler []int func (su *sliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error { var slice []int err := unmarshal(&slice) if err == nil { *su = slice return nil } var intVal int err = unmarshal(&intVal) if err == nil { *su = []int{intVal} return nil } return err } func (s *S) TestUnmarshalerRetry(c *C) { var su sliceUnmarshaler err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su) c.Assert(err, IsNil) c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1, 2, 3})) err = yaml.Unmarshal([]byte("1"), &su) c.Assert(err, IsNil) c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1})) } // From http://yaml.org/type/merge.html var mergeTests = ` anchors: list: - &CENTER { "x": 1, "y": 2 } - &LEFT { "x": 0, "y": 2 } - &BIG { "r": 10 } - &SMALL { "r": 1 } # All the following maps are equal: plain: # Explicit keys "x": 1 "y": 2 "r": 10 label: center/big mergeOne: # Merge one map << : *CENTER "r": 10 label: center/big mergeMultiple: # Merge multiple maps << : [ *CENTER, *BIG ] label: center/big override: # Override << : [ *BIG, *LEFT, *SMALL ] "x": 1 label: center/big shortTag: # Explicit short merge tag !!merge "<<" : [ *CENTER, *BIG ] label: center/big longTag: # Explicit merge long tag ! "<<" : [ *CENTER, *BIG ] label: center/big inlineMap: # Inlined map << : {"x": 1, "y": 2, "r": 10} label: center/big inlineSequenceMap: # Inlined map in sequence << : [ *CENTER, {"r": 10} ] label: center/big ` func (s *S) TestMerge(c *C) { var want = map[interface{}]interface{}{ "x": 1, "y": 2, "r": 10, "label": "center/big", } var m map[interface{}]interface{} err := yaml.Unmarshal([]byte(mergeTests), &m) c.Assert(err, IsNil) for name, test := range m { if name == "anchors" { continue } c.Assert(test, DeepEquals, want, Commentf("test %q failed", name)) } } func (s *S) TestMergeStruct(c *C) { type Data struct { X, Y, R int Label string } want := Data{1, 2, 10, "center/big"} var m map[string]Data err := yaml.Unmarshal([]byte(mergeTests), &m) c.Assert(err, IsNil) for name, test := range m { if name == "anchors" { continue } c.Assert(test, Equals, want, Commentf("test %q failed", name)) } } var unmarshalNullTests = []func() interface{}{ func() interface{} { var v interface{}; v = "v"; return &v }, func() interface{} { var s = "s"; return &s }, func() interface{} { var s = "s"; sptr := &s; return &sptr }, func() interface{} { var i = 1; return &i }, func() interface{} { var i = 1; iptr := &i; return &iptr }, func() interface{} { m := map[string]int{"s": 1}; return &m }, func() interface{} { m := map[string]int{"s": 1}; return m }, } func (s *S) TestUnmarshalNull(c *C) { for _, test := range unmarshalNullTests { item := test() zero := reflect.Zero(reflect.TypeOf(item).Elem()).Interface() err := yaml.Unmarshal([]byte("null"), item) c.Assert(err, IsNil) if reflect.TypeOf(item).Kind() == reflect.Map { c.Assert(reflect.ValueOf(item).Interface(), DeepEquals, reflect.MakeMap(reflect.TypeOf(item)).Interface()) } else { c.Assert(reflect.ValueOf(item).Elem().Interface(), DeepEquals, zero) } } } func (s *S) TestUnmarshalSliceOnPreset(c *C) { // Issue #48. v := struct{ A []int }{[]int{1}} yaml.Unmarshal([]byte("a: [2]"), &v) c.Assert(v.A, DeepEquals, []int{2}) } func (s *S) TestUnmarshalStrict(c *C) { v := struct{ A, B int }{} err := yaml.UnmarshalStrict([]byte("a: 1\nb: 2"), &v) c.Check(err, IsNil) err = yaml.Unmarshal([]byte("a: 1\nb: 2\nc: 3"), &v) c.Check(err, IsNil) err = yaml.UnmarshalStrict([]byte("a: 1\nb: 2\nc: 3"), &v) c.Check(err, ErrorMatches, "yaml: unmarshal errors:\n line 1: field c not found in struct struct { A int; B int }") } //var data []byte //func init() { // var err error // data, err = ioutil.ReadFile("/tmp/file.yaml") // if err != nil { // panic(err) // } //} // //func (s *S) BenchmarkUnmarshal(c *C) { // var err error // for i := 0; i < c.N; i++ { // var v map[string]interface{} // err = yaml.Unmarshal(data, &v) // } // if err != nil { // panic(err) // } //} // //func (s *S) BenchmarkMarshal(c *C) { // var v map[string]interface{} // yaml.Unmarshal(data, &v) // c.ResetTimer() // for i := 0; i < c.N; i++ { // yaml.Marshal(&v) // } //} ================================================ FILE: vendor/gopkg.in/yaml.v2/emitterc.go ================================================ package yaml import ( "bytes" ) // Flush the buffer if needed. func flush(emitter *yaml_emitter_t) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) { return yaml_emitter_flush(emitter) } return true } // Put a character to the output buffer. func put(emitter *yaml_emitter_t, value byte) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { return false } emitter.buffer[emitter.buffer_pos] = value emitter.buffer_pos++ emitter.column++ return true } // Put a line break to the output buffer. func put_break(emitter *yaml_emitter_t) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { return false } switch emitter.line_break { case yaml_CR_BREAK: emitter.buffer[emitter.buffer_pos] = '\r' emitter.buffer_pos += 1 case yaml_LN_BREAK: emitter.buffer[emitter.buffer_pos] = '\n' emitter.buffer_pos += 1 case yaml_CRLN_BREAK: emitter.buffer[emitter.buffer_pos+0] = '\r' emitter.buffer[emitter.buffer_pos+1] = '\n' emitter.buffer_pos += 2 default: panic("unknown line break setting") } emitter.column = 0 emitter.line++ return true } // Copy a character from a string into buffer. func write(emitter *yaml_emitter_t, s []byte, i *int) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { return false } p := emitter.buffer_pos w := width(s[*i]) switch w { case 4: emitter.buffer[p+3] = s[*i+3] fallthrough case 3: emitter.buffer[p+2] = s[*i+2] fallthrough case 2: emitter.buffer[p+1] = s[*i+1] fallthrough case 1: emitter.buffer[p+0] = s[*i+0] default: panic("unknown character width") } emitter.column++ emitter.buffer_pos += w *i += w return true } // Write a whole string into buffer. func write_all(emitter *yaml_emitter_t, s []byte) bool { for i := 0; i < len(s); { if !write(emitter, s, &i) { return false } } return true } // Copy a line break character from a string into buffer. func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { if s[*i] == '\n' { if !put_break(emitter) { return false } *i++ } else { if !write(emitter, s, i) { return false } emitter.column = 0 emitter.line++ } return true } // Set an emitter error and return false. func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool { emitter.error = yaml_EMITTER_ERROR emitter.problem = problem return false } // Emit an event. func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { emitter.events = append(emitter.events, *event) for !yaml_emitter_need_more_events(emitter) { event := &emitter.events[emitter.events_head] if !yaml_emitter_analyze_event(emitter, event) { return false } if !yaml_emitter_state_machine(emitter, event) { return false } yaml_event_delete(event) emitter.events_head++ } return true } // Check if we need to accumulate more events before emitting. // // We accumulate extra // - 1 event for DOCUMENT-START // - 2 events for SEQUENCE-START // - 3 events for MAPPING-START // func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { if emitter.events_head == len(emitter.events) { return true } var accumulate int switch emitter.events[emitter.events_head].typ { case yaml_DOCUMENT_START_EVENT: accumulate = 1 break case yaml_SEQUENCE_START_EVENT: accumulate = 2 break case yaml_MAPPING_START_EVENT: accumulate = 3 break default: return false } if len(emitter.events)-emitter.events_head > accumulate { return false } var level int for i := emitter.events_head; i < len(emitter.events); i++ { switch emitter.events[i].typ { case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT: level++ case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT: level-- } if level == 0 { return false } } return true } // Append a directive to the directives stack. func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool { for i := 0; i < len(emitter.tag_directives); i++ { if bytes.Equal(value.handle, emitter.tag_directives[i].handle) { if allow_duplicates { return true } return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive") } } // [Go] Do we actually need to copy this given garbage collection // and the lack of deallocating destructors? tag_copy := yaml_tag_directive_t{ handle: make([]byte, len(value.handle)), prefix: make([]byte, len(value.prefix)), } copy(tag_copy.handle, value.handle) copy(tag_copy.prefix, value.prefix) emitter.tag_directives = append(emitter.tag_directives, tag_copy) return true } // Increase the indentation level. func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { emitter.indents = append(emitter.indents, emitter.indent) if emitter.indent < 0 { if flow { emitter.indent = emitter.best_indent } else { emitter.indent = 0 } } else if !indentless { emitter.indent += emitter.best_indent } return true } // State dispatcher. func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool { switch emitter.state { default: case yaml_EMIT_STREAM_START_STATE: return yaml_emitter_emit_stream_start(emitter, event) case yaml_EMIT_FIRST_DOCUMENT_START_STATE: return yaml_emitter_emit_document_start(emitter, event, true) case yaml_EMIT_DOCUMENT_START_STATE: return yaml_emitter_emit_document_start(emitter, event, false) case yaml_EMIT_DOCUMENT_CONTENT_STATE: return yaml_emitter_emit_document_content(emitter, event) case yaml_EMIT_DOCUMENT_END_STATE: return yaml_emitter_emit_document_end(emitter, event) case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: return yaml_emitter_emit_flow_sequence_item(emitter, event, true) case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: return yaml_emitter_emit_flow_sequence_item(emitter, event, false) case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: return yaml_emitter_emit_flow_mapping_key(emitter, event, true) case yaml_EMIT_FLOW_MAPPING_KEY_STATE: return yaml_emitter_emit_flow_mapping_key(emitter, event, false) case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: return yaml_emitter_emit_flow_mapping_value(emitter, event, true) case yaml_EMIT_FLOW_MAPPING_VALUE_STATE: return yaml_emitter_emit_flow_mapping_value(emitter, event, false) case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: return yaml_emitter_emit_block_sequence_item(emitter, event, true) case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE: return yaml_emitter_emit_block_sequence_item(emitter, event, false) case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: return yaml_emitter_emit_block_mapping_key(emitter, event, true) case yaml_EMIT_BLOCK_MAPPING_KEY_STATE: return yaml_emitter_emit_block_mapping_key(emitter, event, false) case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: return yaml_emitter_emit_block_mapping_value(emitter, event, true) case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE: return yaml_emitter_emit_block_mapping_value(emitter, event, false) case yaml_EMIT_END_STATE: return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END") } panic("invalid emitter state") } // Expect STREAM-START. func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { if event.typ != yaml_STREAM_START_EVENT { return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START") } if emitter.encoding == yaml_ANY_ENCODING { emitter.encoding = event.encoding if emitter.encoding == yaml_ANY_ENCODING { emitter.encoding = yaml_UTF8_ENCODING } } if emitter.best_indent < 2 || emitter.best_indent > 9 { emitter.best_indent = 2 } if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 { emitter.best_width = 80 } if emitter.best_width < 0 { emitter.best_width = 1<<31 - 1 } if emitter.line_break == yaml_ANY_BREAK { emitter.line_break = yaml_LN_BREAK } emitter.indent = -1 emitter.line = 0 emitter.column = 0 emitter.whitespace = true emitter.indention = true if emitter.encoding != yaml_UTF8_ENCODING { if !yaml_emitter_write_bom(emitter) { return false } } emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE return true } // Expect DOCUMENT-START or STREAM-END. func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if event.typ == yaml_DOCUMENT_START_EVENT { if event.version_directive != nil { if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) { return false } } for i := 0; i < len(event.tag_directives); i++ { tag_directive := &event.tag_directives[i] if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) { return false } if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) { return false } } for i := 0; i < len(default_tag_directives); i++ { tag_directive := &default_tag_directives[i] if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) { return false } } implicit := event.implicit if !first || emitter.canonical { implicit = false } if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) { if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if event.version_directive != nil { implicit = false if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) { return false } if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if len(event.tag_directives) > 0 { implicit = false for i := 0; i < len(event.tag_directives); i++ { tag_directive := &event.tag_directives[i] if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) { return false } if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) { return false } if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) { return false } if !yaml_emitter_write_indent(emitter) { return false } } } if yaml_emitter_check_empty_document(emitter) { implicit = false } if !implicit { if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { return false } if emitter.canonical { if !yaml_emitter_write_indent(emitter) { return false } } } emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE return true } if event.typ == yaml_STREAM_END_EVENT { if emitter.open_ended { if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_flush(emitter) { return false } emitter.state = yaml_EMIT_END_STATE return true } return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") } // Expect the root node. func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) return yaml_emitter_emit_node(emitter, event, true, false, false, false) } // Expect DOCUMENT-END. func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool { if event.typ != yaml_DOCUMENT_END_EVENT { return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") } if !yaml_emitter_write_indent(emitter) { return false } if !event.implicit { // [Go] Allocate the slice elsewhere. if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_flush(emitter) { return false } emitter.state = yaml_EMIT_DOCUMENT_START_STATE emitter.tag_directives = emitter.tag_directives[:0] return true } // Expect a flow item node. func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { return false } if !yaml_emitter_increase_indent(emitter, true, false) { return false } emitter.flow_level++ } if event.typ == yaml_SEQUENCE_END_EVENT { emitter.flow_level-- emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] if emitter.canonical && !first { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { return false } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !first { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } } emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) return yaml_emitter_emit_node(emitter, event, false, true, false, false) } // Expect a flow key node. func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { return false } if !yaml_emitter_increase_indent(emitter, true, false) { return false } emitter.flow_level++ } if event.typ == yaml_MAPPING_END_EVENT { emitter.flow_level-- emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] if emitter.canonical && !first { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { return false } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !first { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } } if !emitter.canonical && yaml_emitter_check_simple_key(emitter) { emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, true) } if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) { return false } emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, false) } // Expect a flow value node. func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { if simple { if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { return false } } else { if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) { return false } } emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, false) } // Expect a block item node. func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) { return false } } if event.typ == yaml_SEQUENCE_END_EVENT { emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) { return false } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) return yaml_emitter_emit_node(emitter, event, false, true, false, false) } // Expect a block key node. func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { if !yaml_emitter_increase_indent(emitter, false, false) { return false } } if event.typ == yaml_MAPPING_END_EVENT { emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !yaml_emitter_write_indent(emitter) { return false } if yaml_emitter_check_simple_key(emitter) { emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, true) } if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) { return false } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, false) } // Expect a block value node. func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { if simple { if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { return false } } else { if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) { return false } } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, false) } // Expect a node. func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, root bool, sequence bool, mapping bool, simple_key bool) bool { emitter.root_context = root emitter.sequence_context = sequence emitter.mapping_context = mapping emitter.simple_key_context = simple_key switch event.typ { case yaml_ALIAS_EVENT: return yaml_emitter_emit_alias(emitter, event) case yaml_SCALAR_EVENT: return yaml_emitter_emit_scalar(emitter, event) case yaml_SEQUENCE_START_EVENT: return yaml_emitter_emit_sequence_start(emitter, event) case yaml_MAPPING_START_EVENT: return yaml_emitter_emit_mapping_start(emitter, event) default: return yaml_emitter_set_emitter_error(emitter, "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS") } } // Expect ALIAS. func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_process_anchor(emitter) { return false } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } // Expect SCALAR. func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_select_scalar_style(emitter, event) { return false } if !yaml_emitter_process_anchor(emitter) { return false } if !yaml_emitter_process_tag(emitter) { return false } if !yaml_emitter_increase_indent(emitter, true, false) { return false } if !yaml_emitter_process_scalar(emitter) { return false } emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } // Expect SEQUENCE-START. func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_process_anchor(emitter) { return false } if !yaml_emitter_process_tag(emitter) { return false } if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || yaml_emitter_check_empty_sequence(emitter) { emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE } else { emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE } return true } // Expect MAPPING-START. func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_process_anchor(emitter) { return false } if !yaml_emitter_process_tag(emitter) { return false } if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE || yaml_emitter_check_empty_mapping(emitter) { emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE } else { emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE } return true } // Check if the document content is an empty scalar. func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool { return false // [Go] Huh? } // Check if the next events represent an empty sequence. func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool { if len(emitter.events)-emitter.events_head < 2 { return false } return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT && emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT } // Check if the next events represent an empty mapping. func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool { if len(emitter.events)-emitter.events_head < 2 { return false } return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT && emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT } // Check if the next node can be expressed as a simple key. func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool { length := 0 switch emitter.events[emitter.events_head].typ { case yaml_ALIAS_EVENT: length += len(emitter.anchor_data.anchor) case yaml_SCALAR_EVENT: if emitter.scalar_data.multiline { return false } length += len(emitter.anchor_data.anchor) + len(emitter.tag_data.handle) + len(emitter.tag_data.suffix) + len(emitter.scalar_data.value) case yaml_SEQUENCE_START_EVENT: if !yaml_emitter_check_empty_sequence(emitter) { return false } length += len(emitter.anchor_data.anchor) + len(emitter.tag_data.handle) + len(emitter.tag_data.suffix) case yaml_MAPPING_START_EVENT: if !yaml_emitter_check_empty_mapping(emitter) { return false } length += len(emitter.anchor_data.anchor) + len(emitter.tag_data.handle) + len(emitter.tag_data.suffix) default: return false } return length <= 128 } // Determine an acceptable scalar style. func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool { no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 if no_tag && !event.implicit && !event.quoted_implicit { return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified") } style := event.scalar_style() if style == yaml_ANY_SCALAR_STYLE { style = yaml_PLAIN_SCALAR_STYLE } if emitter.canonical { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } if emitter.simple_key_context && emitter.scalar_data.multiline { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } if style == yaml_PLAIN_SCALAR_STYLE { if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed || emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed { style = yaml_SINGLE_QUOTED_SCALAR_STYLE } if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) { style = yaml_SINGLE_QUOTED_SCALAR_STYLE } if no_tag && !event.implicit { style = yaml_SINGLE_QUOTED_SCALAR_STYLE } } if style == yaml_SINGLE_QUOTED_SCALAR_STYLE { if !emitter.scalar_data.single_quoted_allowed { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } } if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE { if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } } if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE { emitter.tag_data.handle = []byte{'!'} } emitter.scalar_data.style = style return true } // Write an achor. func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool { if emitter.anchor_data.anchor == nil { return true } c := []byte{'&'} if emitter.anchor_data.alias { c[0] = '*' } if !yaml_emitter_write_indicator(emitter, c, true, false, false) { return false } return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor) } // Write a tag. func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool { if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 { return true } if len(emitter.tag_data.handle) > 0 { if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) { return false } if len(emitter.tag_data.suffix) > 0 { if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { return false } } } else { // [Go] Allocate these slices elsewhere. if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) { return false } if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { return false } if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) { return false } } return true } // Write a scalar. func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { switch emitter.scalar_data.style { case yaml_PLAIN_SCALAR_STYLE: return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) case yaml_SINGLE_QUOTED_SCALAR_STYLE: return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) case yaml_DOUBLE_QUOTED_SCALAR_STYLE: return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) case yaml_LITERAL_SCALAR_STYLE: return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value) case yaml_FOLDED_SCALAR_STYLE: return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value) } panic("unknown scalar style") } // Check if a %YAML directive is valid. func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { if version_directive.major != 1 || version_directive.minor != 1 { return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive") } return true } // Check if a %TAG directive is valid. func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool { handle := tag_directive.handle prefix := tag_directive.prefix if len(handle) == 0 { return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty") } if handle[0] != '!' { return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'") } if handle[len(handle)-1] != '!' { return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'") } for i := 1; i < len(handle)-1; i += width(handle[i]) { if !is_alpha(handle, i) { return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only") } } if len(prefix) == 0 { return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty") } return true } // Check if an anchor is valid. func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool { if len(anchor) == 0 { problem := "anchor value must not be empty" if alias { problem = "alias value must not be empty" } return yaml_emitter_set_emitter_error(emitter, problem) } for i := 0; i < len(anchor); i += width(anchor[i]) { if !is_alpha(anchor, i) { problem := "anchor value must contain alphanumerical characters only" if alias { problem = "alias value must contain alphanumerical characters only" } return yaml_emitter_set_emitter_error(emitter, problem) } } emitter.anchor_data.anchor = anchor emitter.anchor_data.alias = alias return true } // Check if a tag is valid. func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { if len(tag) == 0 { return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty") } for i := 0; i < len(emitter.tag_directives); i++ { tag_directive := &emitter.tag_directives[i] if bytes.HasPrefix(tag, tag_directive.prefix) { emitter.tag_data.handle = tag_directive.handle emitter.tag_data.suffix = tag[len(tag_directive.prefix):] return true } } emitter.tag_data.suffix = tag return true } // Check if a scalar is valid. func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { var ( block_indicators = false flow_indicators = false line_breaks = false special_characters = false leading_space = false leading_break = false trailing_space = false trailing_break = false break_space = false space_break = false preceded_by_whitespace = false followed_by_whitespace = false previous_space = false previous_break = false ) emitter.scalar_data.value = value if len(value) == 0 { emitter.scalar_data.multiline = false emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = true emitter.scalar_data.single_quoted_allowed = true emitter.scalar_data.block_allowed = false return true } if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) { block_indicators = true flow_indicators = true } preceded_by_whitespace = true for i, w := 0, 0; i < len(value); i += w { w = width(value[i]) followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) if i == 0 { switch value[i] { case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`': flow_indicators = true block_indicators = true case '?', ':': flow_indicators = true if followed_by_whitespace { block_indicators = true } case '-': if followed_by_whitespace { flow_indicators = true block_indicators = true } } } else { switch value[i] { case ',', '?', '[', ']', '{', '}': flow_indicators = true case ':': flow_indicators = true if followed_by_whitespace { block_indicators = true } case '#': if preceded_by_whitespace { flow_indicators = true block_indicators = true } } } if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { special_characters = true } if is_space(value, i) { if i == 0 { leading_space = true } if i+width(value[i]) == len(value) { trailing_space = true } if previous_break { break_space = true } previous_space = true previous_break = false } else if is_break(value, i) { line_breaks = true if i == 0 { leading_break = true } if i+width(value[i]) == len(value) { trailing_break = true } if previous_space { space_break = true } previous_space = false previous_break = true } else { previous_space = false previous_break = false } // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. preceded_by_whitespace = is_blankz(value, i) } emitter.scalar_data.multiline = line_breaks emitter.scalar_data.flow_plain_allowed = true emitter.scalar_data.block_plain_allowed = true emitter.scalar_data.single_quoted_allowed = true emitter.scalar_data.block_allowed = true if leading_space || leading_break || trailing_space || trailing_break { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false } if trailing_space { emitter.scalar_data.block_allowed = false } if break_space { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false emitter.scalar_data.single_quoted_allowed = false } if space_break || special_characters { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false emitter.scalar_data.single_quoted_allowed = false emitter.scalar_data.block_allowed = false } if line_breaks { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false } if flow_indicators { emitter.scalar_data.flow_plain_allowed = false } if block_indicators { emitter.scalar_data.block_plain_allowed = false } return true } // Check if the event data is valid. func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { emitter.anchor_data.anchor = nil emitter.tag_data.handle = nil emitter.tag_data.suffix = nil emitter.scalar_data.value = nil switch event.typ { case yaml_ALIAS_EVENT: if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { return false } case yaml_SCALAR_EVENT: if len(event.anchor) > 0 { if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { return false } } if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) { if !yaml_emitter_analyze_tag(emitter, event.tag) { return false } } if !yaml_emitter_analyze_scalar(emitter, event.value) { return false } case yaml_SEQUENCE_START_EVENT: if len(event.anchor) > 0 { if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { return false } } if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { if !yaml_emitter_analyze_tag(emitter, event.tag) { return false } } case yaml_MAPPING_START_EVENT: if len(event.anchor) > 0 { if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { return false } } if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { if !yaml_emitter_analyze_tag(emitter, event.tag) { return false } } } return true } // Write the BOM character. func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool { if !flush(emitter) { return false } pos := emitter.buffer_pos emitter.buffer[pos+0] = '\xEF' emitter.buffer[pos+1] = '\xBB' emitter.buffer[pos+2] = '\xBF' emitter.buffer_pos += 3 return true } func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { indent := emitter.indent if indent < 0 { indent = 0 } if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) { if !put_break(emitter) { return false } } for emitter.column < indent { if !put(emitter, ' ') { return false } } emitter.whitespace = true emitter.indention = true return true } func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool { if need_whitespace && !emitter.whitespace { if !put(emitter, ' ') { return false } } if !write_all(emitter, indicator) { return false } emitter.whitespace = is_whitespace emitter.indention = (emitter.indention && is_indention) emitter.open_ended = false return true } func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool { if !write_all(emitter, value) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool { if !emitter.whitespace { if !put(emitter, ' ') { return false } } if !write_all(emitter, value) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool { if need_whitespace && !emitter.whitespace { if !put(emitter, ' ') { return false } } for i := 0; i < len(value); { var must_write bool switch value[i] { case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']': must_write = true default: must_write = is_alpha(value, i) } if must_write { if !write(emitter, value, &i) { return false } } else { w := width(value[i]) for k := 0; k < w; k++ { octet := value[i] i++ if !put(emitter, '%') { return false } c := octet >> 4 if c < 10 { c += '0' } else { c += 'A' - 10 } if !put(emitter, c) { return false } c = octet & 0x0f if c < 10 { c += '0' } else { c += 'A' - 10 } if !put(emitter, c) { return false } } } } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { if !emitter.whitespace { if !put(emitter, ' ') { return false } } spaces := false breaks := false for i := 0; i < len(value); { if is_space(value, i) { if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) { if !yaml_emitter_write_indent(emitter) { return false } i += width(value[i]) } else { if !write(emitter, value, &i) { return false } } spaces = true } else if is_break(value, i) { if !breaks && value[i] == '\n' { if !put_break(emitter) { return false } } if !write_break(emitter, value, &i) { return false } emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } } if !write(emitter, value, &i) { return false } emitter.indention = false spaces = false breaks = false } } emitter.whitespace = false emitter.indention = false if emitter.root_context { emitter.open_ended = true } return true } func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) { return false } spaces := false breaks := false for i := 0; i < len(value); { if is_space(value, i) { if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) { if !yaml_emitter_write_indent(emitter) { return false } i += width(value[i]) } else { if !write(emitter, value, &i) { return false } } spaces = true } else if is_break(value, i) { if !breaks && value[i] == '\n' { if !put_break(emitter) { return false } } if !write_break(emitter, value, &i) { return false } emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } } if value[i] == '\'' { if !put(emitter, '\'') { return false } } if !write(emitter, value, &i) { return false } emitter.indention = false spaces = false breaks = false } } if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { spaces := false if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) { return false } for i := 0; i < len(value); { if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) || is_bom(value, i) || is_break(value, i) || value[i] == '"' || value[i] == '\\' { octet := value[i] var w int var v rune switch { case octet&0x80 == 0x00: w, v = 1, rune(octet&0x7F) case octet&0xE0 == 0xC0: w, v = 2, rune(octet&0x1F) case octet&0xF0 == 0xE0: w, v = 3, rune(octet&0x0F) case octet&0xF8 == 0xF0: w, v = 4, rune(octet&0x07) } for k := 1; k < w; k++ { octet = value[i+k] v = (v << 6) + (rune(octet) & 0x3F) } i += w if !put(emitter, '\\') { return false } var ok bool switch v { case 0x00: ok = put(emitter, '0') case 0x07: ok = put(emitter, 'a') case 0x08: ok = put(emitter, 'b') case 0x09: ok = put(emitter, 't') case 0x0A: ok = put(emitter, 'n') case 0x0b: ok = put(emitter, 'v') case 0x0c: ok = put(emitter, 'f') case 0x0d: ok = put(emitter, 'r') case 0x1b: ok = put(emitter, 'e') case 0x22: ok = put(emitter, '"') case 0x5c: ok = put(emitter, '\\') case 0x85: ok = put(emitter, 'N') case 0xA0: ok = put(emitter, '_') case 0x2028: ok = put(emitter, 'L') case 0x2029: ok = put(emitter, 'P') default: if v <= 0xFF { ok = put(emitter, 'x') w = 2 } else if v <= 0xFFFF { ok = put(emitter, 'u') w = 4 } else { ok = put(emitter, 'U') w = 8 } for k := (w - 1) * 4; ok && k >= 0; k -= 4 { digit := byte((v >> uint(k)) & 0x0F) if digit < 10 { ok = put(emitter, digit+'0') } else { ok = put(emitter, digit+'A'-10) } } } if !ok { return false } spaces = false } else if is_space(value, i) { if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 { if !yaml_emitter_write_indent(emitter) { return false } if is_space(value, i+1) { if !put(emitter, '\\') { return false } } i += width(value[i]) } else if !write(emitter, value, &i) { return false } spaces = true } else { if !write(emitter, value, &i) { return false } spaces = false } } if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool { if is_space(value, 0) || is_break(value, 0) { indent_hint := []byte{'0' + byte(emitter.best_indent)} if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) { return false } } emitter.open_ended = false var chomp_hint [1]byte if len(value) == 0 { chomp_hint[0] = '-' } else { i := len(value) - 1 for value[i]&0xC0 == 0x80 { i-- } if !is_break(value, i) { chomp_hint[0] = '-' } else if i == 0 { chomp_hint[0] = '+' emitter.open_ended = true } else { i-- for value[i]&0xC0 == 0x80 { i-- } if is_break(value, i) { chomp_hint[0] = '+' emitter.open_ended = true } } } if chomp_hint[0] != 0 { if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) { return false } } return true } func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool { if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) { return false } if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } if !put_break(emitter) { return false } emitter.indention = true emitter.whitespace = true breaks := true for i := 0; i < len(value); { if is_break(value, i) { if !write_break(emitter, value, &i) { return false } emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } } if !write(emitter, value, &i) { return false } emitter.indention = false breaks = false } } return true } func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool { if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) { return false } if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } if !put_break(emitter) { return false } emitter.indention = true emitter.whitespace = true breaks := true leading_spaces := true for i := 0; i < len(value); { if is_break(value, i) { if !breaks && !leading_spaces && value[i] == '\n' { k := 0 for is_break(value, k) { k += width(value[k]) } if !is_blankz(value, k) { if !put_break(emitter) { return false } } } if !write_break(emitter, value, &i) { return false } emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } leading_spaces = is_blank(value, i) } if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } i += width(value[i]) } else { if !write(emitter, value, &i) { return false } } emitter.indention = false breaks = false } } return true } ================================================ FILE: vendor/gopkg.in/yaml.v2/encode.go ================================================ package yaml import ( "encoding" "fmt" "reflect" "regexp" "sort" "strconv" "strings" "time" ) type encoder struct { emitter yaml_emitter_t event yaml_event_t out []byte flow bool } func newEncoder() (e *encoder) { e = &encoder{} e.must(yaml_emitter_initialize(&e.emitter)) yaml_emitter_set_output_string(&e.emitter, &e.out) yaml_emitter_set_unicode(&e.emitter, true) e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)) e.emit() e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true)) e.emit() return e } func (e *encoder) finish() { e.must(yaml_document_end_event_initialize(&e.event, true)) e.emit() e.emitter.open_ended = false e.must(yaml_stream_end_event_initialize(&e.event)) e.emit() } func (e *encoder) destroy() { yaml_emitter_delete(&e.emitter) } func (e *encoder) emit() { // This will internally delete the e.event value. if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT { e.must(false) } } func (e *encoder) must(ok bool) { if !ok { msg := e.emitter.problem if msg == "" { msg = "unknown problem generating YAML content" } failf("%s", msg) } } func (e *encoder) marshal(tag string, in reflect.Value) { if !in.IsValid() { e.nilv() return } iface := in.Interface() if m, ok := iface.(Marshaler); ok { v, err := m.MarshalYAML() if err != nil { fail(err) } if v == nil { e.nilv() return } in = reflect.ValueOf(v) } else if m, ok := iface.(encoding.TextMarshaler); ok { text, err := m.MarshalText() if err != nil { fail(err) } in = reflect.ValueOf(string(text)) } switch in.Kind() { case reflect.Interface: if in.IsNil() { e.nilv() } else { e.marshal(tag, in.Elem()) } case reflect.Map: e.mapv(tag, in) case reflect.Ptr: if in.IsNil() { e.nilv() } else { e.marshal(tag, in.Elem()) } case reflect.Struct: e.structv(tag, in) case reflect.Slice: if in.Type().Elem() == mapItemType { e.itemsv(tag, in) } else { e.slicev(tag, in) } case reflect.String: e.stringv(tag, in) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if in.Type() == durationType { e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) } else { e.intv(tag, in) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: e.uintv(tag, in) case reflect.Float32, reflect.Float64: e.floatv(tag, in) case reflect.Bool: e.boolv(tag, in) default: panic("cannot marshal type: " + in.Type().String()) } } func (e *encoder) mapv(tag string, in reflect.Value) { e.mappingv(tag, func() { keys := keyList(in.MapKeys()) sort.Sort(keys) for _, k := range keys { e.marshal("", k) e.marshal("", in.MapIndex(k)) } }) } func (e *encoder) itemsv(tag string, in reflect.Value) { e.mappingv(tag, func() { slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) for _, item := range slice { e.marshal("", reflect.ValueOf(item.Key)) e.marshal("", reflect.ValueOf(item.Value)) } }) } func (e *encoder) structv(tag string, in reflect.Value) { sinfo, err := getStructInfo(in.Type()) if err != nil { panic(err) } e.mappingv(tag, func() { for _, info := range sinfo.FieldsList { var value reflect.Value if info.Inline == nil { value = in.Field(info.Num) } else { value = in.FieldByIndex(info.Inline) } if info.OmitEmpty && isZero(value) { continue } e.marshal("", reflect.ValueOf(info.Key)) e.flow = info.Flow e.marshal("", value) } if sinfo.InlineMap >= 0 { m := in.Field(sinfo.InlineMap) if m.Len() > 0 { e.flow = false keys := keyList(m.MapKeys()) sort.Sort(keys) for _, k := range keys { if _, found := sinfo.FieldsMap[k.String()]; found { panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) } e.marshal("", k) e.flow = false e.marshal("", m.MapIndex(k)) } } } }) } func (e *encoder) mappingv(tag string, f func()) { implicit := tag == "" style := yaml_BLOCK_MAPPING_STYLE if e.flow { e.flow = false style = yaml_FLOW_MAPPING_STYLE } e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) e.emit() f() e.must(yaml_mapping_end_event_initialize(&e.event)) e.emit() } func (e *encoder) slicev(tag string, in reflect.Value) { implicit := tag == "" style := yaml_BLOCK_SEQUENCE_STYLE if e.flow { e.flow = false style = yaml_FLOW_SEQUENCE_STYLE } e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) e.emit() n := in.Len() for i := 0; i < n; i++ { e.marshal("", in.Index(i)) } e.must(yaml_sequence_end_event_initialize(&e.event)) e.emit() } // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. // // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported // in YAML 1.2 and by this package, but these should be marshalled quoted for // the time being for compatibility with other parsers. func isBase60Float(s string) (result bool) { // Fast path. if s == "" { return false } c := s[0] if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { return false } // Do the full match. return base60float.MatchString(s) } // From http://yaml.org/type/float.html, except the regular expression there // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) func (e *encoder) stringv(tag string, in reflect.Value) { var style yaml_scalar_style_t s := in.String() rtag, rs := resolve("", s) if rtag == yaml_BINARY_TAG { if tag == "" || tag == yaml_STR_TAG { tag = rtag s = rs.(string) } else if tag == yaml_BINARY_TAG { failf("explicitly tagged !!binary data must be base64-encoded") } else { failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) } } if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } else if strings.Contains(s, "\n") { style = yaml_LITERAL_SCALAR_STYLE } else { style = yaml_PLAIN_SCALAR_STYLE } e.emitScalar(s, "", tag, style) } func (e *encoder) boolv(tag string, in reflect.Value) { var s string if in.Bool() { s = "true" } else { s = "false" } e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) } func (e *encoder) intv(tag string, in reflect.Value) { s := strconv.FormatInt(in.Int(), 10) e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) } func (e *encoder) uintv(tag string, in reflect.Value) { s := strconv.FormatUint(in.Uint(), 10) e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) } func (e *encoder) floatv(tag string, in reflect.Value) { // FIXME: Handle 64 bits here. s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32) switch s { case "+Inf": s = ".inf" case "-Inf": s = "-.inf" case "NaN": s = ".nan" } e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) } func (e *encoder) nilv() { e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) } func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { implicit := tag == "" e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) e.emit() } ================================================ FILE: vendor/gopkg.in/yaml.v2/encode_test.go ================================================ package yaml_test import ( "fmt" "math" "strconv" "strings" "time" . "gopkg.in/check.v1" "gopkg.in/yaml.v2" "net" "os" ) var marshalIntTest = 123 var marshalTests = []struct { value interface{} data string }{ { nil, "null\n", }, { &struct{}{}, "{}\n", }, { map[string]string{"v": "hi"}, "v: hi\n", }, { map[string]interface{}{"v": "hi"}, "v: hi\n", }, { map[string]string{"v": "true"}, "v: \"true\"\n", }, { map[string]string{"v": "false"}, "v: \"false\"\n", }, { map[string]interface{}{"v": true}, "v: true\n", }, { map[string]interface{}{"v": false}, "v: false\n", }, { map[string]interface{}{"v": 10}, "v: 10\n", }, { map[string]interface{}{"v": -10}, "v: -10\n", }, { map[string]uint{"v": 42}, "v: 42\n", }, { map[string]interface{}{"v": int64(4294967296)}, "v: 4294967296\n", }, { map[string]int64{"v": int64(4294967296)}, "v: 4294967296\n", }, { map[string]uint64{"v": 4294967296}, "v: 4294967296\n", }, { map[string]interface{}{"v": "10"}, "v: \"10\"\n", }, { map[string]interface{}{"v": 0.1}, "v: 0.1\n", }, { map[string]interface{}{"v": float64(0.1)}, "v: 0.1\n", }, { map[string]interface{}{"v": -0.1}, "v: -0.1\n", }, { map[string]interface{}{"v": math.Inf(+1)}, "v: .inf\n", }, { map[string]interface{}{"v": math.Inf(-1)}, "v: -.inf\n", }, { map[string]interface{}{"v": math.NaN()}, "v: .nan\n", }, { map[string]interface{}{"v": nil}, "v: null\n", }, { map[string]interface{}{"v": ""}, "v: \"\"\n", }, { map[string][]string{"v": []string{"A", "B"}}, "v:\n- A\n- B\n", }, { map[string][]string{"v": []string{"A", "B\nC"}}, "v:\n- A\n- |-\n B\n C\n", }, { map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}}, "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", }, { map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, "a:\n b: c\n", }, { map[string]interface{}{"a": "-"}, "a: '-'\n", }, // Simple values. { &marshalIntTest, "123\n", }, // Structures { &struct{ Hello string }{"world"}, "hello: world\n", }, { &struct { A struct { B string } }{struct{ B string }{"c"}}, "a:\n b: c\n", }, { &struct { A *struct { B string } }{&struct{ B string }{"c"}}, "a:\n b: c\n", }, { &struct { A *struct { B string } }{}, "a: null\n", }, { &struct{ A int }{1}, "a: 1\n", }, { &struct{ A []int }{[]int{1, 2}}, "a:\n- 1\n- 2\n", }, { &struct { B int "a" }{1}, "a: 1\n", }, { &struct{ A bool }{true}, "a: true\n", }, // Conditional flag { &struct { A int "a,omitempty" B int "b,omitempty" }{1, 0}, "a: 1\n", }, { &struct { A int "a,omitempty" B int "b,omitempty" }{0, 0}, "{}\n", }, { &struct { A *struct{ X, y int } "a,omitempty,flow" }{&struct{ X, y int }{1, 2}}, "a: {x: 1}\n", }, { &struct { A *struct{ X, y int } "a,omitempty,flow" }{nil}, "{}\n", }, { &struct { A *struct{ X, y int } "a,omitempty,flow" }{&struct{ X, y int }{}}, "a: {x: 0}\n", }, { &struct { A struct{ X, y int } "a,omitempty,flow" }{struct{ X, y int }{1, 2}}, "a: {x: 1}\n", }, { &struct { A struct{ X, y int } "a,omitempty,flow" }{struct{ X, y int }{0, 1}}, "{}\n", }, { &struct { A float64 "a,omitempty" B float64 "b,omitempty" }{1, 0}, "a: 1\n", }, // Flow flag { &struct { A []int "a,flow" }{[]int{1, 2}}, "a: [1, 2]\n", }, { &struct { A map[string]string "a,flow" }{map[string]string{"b": "c", "d": "e"}}, "a: {b: c, d: e}\n", }, { &struct { A struct { B, D string } "a,flow" }{struct{ B, D string }{"c", "e"}}, "a: {b: c, d: e}\n", }, // Unexported field { &struct { u int A int }{0, 1}, "a: 1\n", }, // Ignored field { &struct { A int B int "-" }{1, 2}, "a: 1\n", }, // Struct inlining { &struct { A int C inlineB `yaml:",inline"` }{1, inlineB{2, inlineC{3}}}, "a: 1\nb: 2\nc: 3\n", }, // Map inlining { &struct { A int C map[string]int `yaml:",inline"` }{1, map[string]int{"b": 2, "c": 3}}, "a: 1\nb: 2\nc: 3\n", }, // Duration { map[string]time.Duration{"a": 3 * time.Second}, "a: 3s\n", }, // Issue #24: bug in map merging logic. { map[string]string{"a": ""}, "a: \n", }, // Issue #34: marshal unsupported base 60 floats quoted for compatibility // with old YAML 1.1 parsers. { map[string]string{"a": "1:1"}, "a: \"1:1\"\n", }, // Binary data. { map[string]string{"a": "\x00"}, "a: \"\\0\"\n", }, { map[string]string{"a": "\x80\x81\x82"}, "a: !!binary gIGC\n", }, { map[string]string{"a": strings.Repeat("\x90", 54)}, "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", }, // Ordered maps. { &yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}}, "b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n", }, // Encode unicode as utf-8 rather than in escaped form. { map[string]string{"a": "你好"}, "a: 你好\n", }, // Support encoding.TextMarshaler. { map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, "a: 1.2.3.4\n", }, { map[string]time.Time{"a": time.Unix(1424801979, 0)}, "a: 2015-02-24T18:19:39Z\n", }, // Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible). { map[string]string{"a": "b: c"}, "a: 'b: c'\n", }, // Containing hash mark ('#') in string should be quoted { map[string]string{"a": "Hello #comment"}, "a: 'Hello #comment'\n", }, { map[string]string{"a": "你好 #comment"}, "a: '你好 #comment'\n", }, } func (s *S) TestMarshal(c *C) { defer os.Setenv("TZ", os.Getenv("TZ")) os.Setenv("TZ", "UTC") for _, item := range marshalTests { data, err := yaml.Marshal(item.value) c.Assert(err, IsNil) c.Assert(string(data), Equals, item.data) } } var marshalErrorTests = []struct { value interface{} error string panic string }{{ value: &struct { B int inlineB ",inline" }{1, inlineB{2, inlineC{3}}}, panic: `Duplicated key 'b' in struct struct \{ B int; .*`, }, { value: &struct { A int B map[string]int ",inline" }{1, map[string]int{"a": 2}}, panic: `Can't have key "a" in inlined map; conflicts with struct field`, }} func (s *S) TestMarshalErrors(c *C) { for _, item := range marshalErrorTests { if item.panic != "" { c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic) } else { _, err := yaml.Marshal(item.value) c.Assert(err, ErrorMatches, item.error) } } } func (s *S) TestMarshalTypeCache(c *C) { var data []byte var err error func() { type T struct{ A int } data, err = yaml.Marshal(&T{}) c.Assert(err, IsNil) }() func() { type T struct{ B int } data, err = yaml.Marshal(&T{}) c.Assert(err, IsNil) }() c.Assert(string(data), Equals, "b: 0\n") } var marshalerTests = []struct { data string value interface{} }{ {"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}}, {"_:\n- 1\n- A\n", []interface{}{1, "A"}}, {"_: 10\n", 10}, {"_: null\n", nil}, {"_: BAR!\n", "BAR!"}, } type marshalerType struct { value interface{} } func (o marshalerType) MarshalText() ([]byte, error) { panic("MarshalText called on type with MarshalYAML") } func (o marshalerType) MarshalYAML() (interface{}, error) { return o.value, nil } type marshalerValue struct { Field marshalerType "_" } func (s *S) TestMarshaler(c *C) { for _, item := range marshalerTests { obj := &marshalerValue{} obj.Field.value = item.value data, err := yaml.Marshal(obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, string(item.data)) } } func (s *S) TestMarshalerWholeDocument(c *C) { obj := &marshalerType{} obj.value = map[string]string{"hello": "world!"} data, err := yaml.Marshal(obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, "hello: world!\n") } type failingMarshaler struct{} func (ft *failingMarshaler) MarshalYAML() (interface{}, error) { return nil, failingErr } func (s *S) TestMarshalerError(c *C) { _, err := yaml.Marshal(&failingMarshaler{}) c.Assert(err, Equals, failingErr) } func (s *S) TestSortedOutput(c *C) { order := []interface{}{ false, true, 1, uint(1), 1.0, 1.1, 1.2, 2, uint(2), 2.0, 2.1, "", ".1", ".2", ".a", "1", "2", "a!10", "a/2", "a/10", "a~10", "ab/1", "b/1", "b/01", "b/2", "b/02", "b/3", "b/03", "b1", "b01", "b3", "c2.10", "c10.2", "d1", "d12", "d12a", } m := make(map[interface{}]int) for _, k := range order { m[k] = 1 } data, err := yaml.Marshal(m) c.Assert(err, IsNil) out := "\n" + string(data) last := 0 for i, k := range order { repr := fmt.Sprint(k) if s, ok := k.(string); ok { if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil { repr = `"` + repr + `"` } } index := strings.Index(out, "\n"+repr+":") if index == -1 { c.Fatalf("%#v is not in the output: %#v", k, out) } if index < last { c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out) } last = index } } ================================================ FILE: vendor/gopkg.in/yaml.v2/example_embedded_test.go ================================================ package yaml_test import ( "fmt" "log" "gopkg.in/yaml.v2" ) // An example showing how to unmarshal embedded // structs from YAML. type StructA struct { A string `yaml:"a"` } type StructB struct { // Embedded structs are not treated as embedded in YAML by default. To do that, // add the ",inline" annotation below StructA `yaml:",inline"` B string `yaml:"b"` } var data = ` a: a string from struct A b: a string from struct B ` func ExampleUnmarshal_embedded() { var b StructB err := yaml.Unmarshal([]byte(data), &b) if err != nil { log.Fatal("cannot unmarshal data: %v", err) } fmt.Println(b.A) fmt.Println(b.B) // Output: // a string from struct A // a string from struct B } ================================================ FILE: vendor/gopkg.in/yaml.v2/parserc.go ================================================ package yaml import ( "bytes" ) // The parser implements the following grammar: // // stream ::= STREAM-START implicit_document? explicit_document* STREAM-END // implicit_document ::= block_node DOCUMENT-END* // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // block_node_or_indentless_sequence ::= // ALIAS // | properties (block_content | indentless_block_sequence)? // | block_content // | indentless_block_sequence // block_node ::= ALIAS // | properties block_content? // | block_content // flow_node ::= ALIAS // | properties flow_content? // | flow_content // properties ::= TAG ANCHOR? | ANCHOR TAG? // block_content ::= block_collection | flow_collection | SCALAR // flow_content ::= flow_collection | SCALAR // block_collection ::= block_sequence | block_mapping // flow_collection ::= flow_sequence | flow_mapping // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END // indentless_sequence ::= (BLOCK-ENTRY block_node?)+ // block_mapping ::= BLOCK-MAPPING_START // ((KEY block_node_or_indentless_sequence?)? // (VALUE block_node_or_indentless_sequence?)?)* // BLOCK-END // flow_sequence ::= FLOW-SEQUENCE-START // (flow_sequence_entry FLOW-ENTRY)* // flow_sequence_entry? // FLOW-SEQUENCE-END // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // flow_mapping ::= FLOW-MAPPING-START // (flow_mapping_entry FLOW-ENTRY)* // flow_mapping_entry? // FLOW-MAPPING-END // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // Peek the next token in the token queue. func peek_token(parser *yaml_parser_t) *yaml_token_t { if parser.token_available || yaml_parser_fetch_more_tokens(parser) { return &parser.tokens[parser.tokens_head] } return nil } // Remove the next token from the queue (must be called after peek_token). func skip_token(parser *yaml_parser_t) { parser.token_available = false parser.tokens_parsed++ parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN parser.tokens_head++ } // Get the next event. func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool { // Erase the event object. *event = yaml_event_t{} // No events after the end of the stream or error. if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE { return true } // Generate the next event. return yaml_parser_state_machine(parser, event) } // Set parser error. func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool { parser.error = yaml_PARSER_ERROR parser.problem = problem parser.problem_mark = problem_mark return false } func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool { parser.error = yaml_PARSER_ERROR parser.context = context parser.context_mark = context_mark parser.problem = problem parser.problem_mark = problem_mark return false } // State dispatcher. func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool { //trace("yaml_parser_state_machine", "state:", parser.state.String()) switch parser.state { case yaml_PARSE_STREAM_START_STATE: return yaml_parser_parse_stream_start(parser, event) case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: return yaml_parser_parse_document_start(parser, event, true) case yaml_PARSE_DOCUMENT_START_STATE: return yaml_parser_parse_document_start(parser, event, false) case yaml_PARSE_DOCUMENT_CONTENT_STATE: return yaml_parser_parse_document_content(parser, event) case yaml_PARSE_DOCUMENT_END_STATE: return yaml_parser_parse_document_end(parser, event) case yaml_PARSE_BLOCK_NODE_STATE: return yaml_parser_parse_node(parser, event, true, false) case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: return yaml_parser_parse_node(parser, event, true, true) case yaml_PARSE_FLOW_NODE_STATE: return yaml_parser_parse_node(parser, event, false, false) case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: return yaml_parser_parse_block_sequence_entry(parser, event, true) case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_block_sequence_entry(parser, event, false) case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_indentless_sequence_entry(parser, event) case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: return yaml_parser_parse_block_mapping_key(parser, event, true) case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: return yaml_parser_parse_block_mapping_key(parser, event, false) case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: return yaml_parser_parse_block_mapping_value(parser, event) case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: return yaml_parser_parse_flow_sequence_entry(parser, event, true) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_flow_sequence_entry(parser, event, false) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event) case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: return yaml_parser_parse_flow_mapping_key(parser, event, true) case yaml_PARSE_FLOW_MAPPING_KEY_STATE: return yaml_parser_parse_flow_mapping_key(parser, event, false) case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: return yaml_parser_parse_flow_mapping_value(parser, event, false) case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: return yaml_parser_parse_flow_mapping_value(parser, event, true) default: panic("invalid parser state") } } // Parse the production: // stream ::= STREAM-START implicit_document? explicit_document* STREAM-END // ************ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ != yaml_STREAM_START_TOKEN { return yaml_parser_set_parser_error(parser, "did not find expected ", token.start_mark) } parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE *event = yaml_event_t{ typ: yaml_STREAM_START_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, encoding: token.encoding, } skip_token(parser) return true } // Parse the productions: // implicit_document ::= block_node DOCUMENT-END* // * // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // ************************* func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { token := peek_token(parser) if token == nil { return false } // Parse extra document end indicators. if !implicit { for token.typ == yaml_DOCUMENT_END_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } } } if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN && token.typ != yaml_TAG_DIRECTIVE_TOKEN && token.typ != yaml_DOCUMENT_START_TOKEN && token.typ != yaml_STREAM_END_TOKEN { // Parse an implicit document. if !yaml_parser_process_directives(parser, nil, nil) { return false } parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) parser.state = yaml_PARSE_BLOCK_NODE_STATE *event = yaml_event_t{ typ: yaml_DOCUMENT_START_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } } else if token.typ != yaml_STREAM_END_TOKEN { // Parse an explicit document. var version_directive *yaml_version_directive_t var tag_directives []yaml_tag_directive_t start_mark := token.start_mark if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) { return false } token = peek_token(parser) if token == nil { return false } if token.typ != yaml_DOCUMENT_START_TOKEN { yaml_parser_set_parser_error(parser, "did not find expected ", token.start_mark) return false } parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE end_mark := token.end_mark *event = yaml_event_t{ typ: yaml_DOCUMENT_START_EVENT, start_mark: start_mark, end_mark: end_mark, version_directive: version_directive, tag_directives: tag_directives, implicit: false, } skip_token(parser) } else { // Parse the stream end. parser.state = yaml_PARSE_END_STATE *event = yaml_event_t{ typ: yaml_STREAM_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) } return true } // Parse the productions: // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // *********** // func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN || token.typ == yaml_DOCUMENT_START_TOKEN || token.typ == yaml_DOCUMENT_END_TOKEN || token.typ == yaml_STREAM_END_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } return yaml_parser_parse_node(parser, event, true, false) } // Parse the productions: // implicit_document ::= block_node DOCUMENT-END* // ************* // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } start_mark := token.start_mark end_mark := token.start_mark implicit := true if token.typ == yaml_DOCUMENT_END_TOKEN { end_mark = token.end_mark skip_token(parser) implicit = false } parser.tag_directives = parser.tag_directives[:0] parser.state = yaml_PARSE_DOCUMENT_START_STATE *event = yaml_event_t{ typ: yaml_DOCUMENT_END_EVENT, start_mark: start_mark, end_mark: end_mark, implicit: implicit, } return true } // Parse the productions: // block_node_or_indentless_sequence ::= // ALIAS // ***** // | properties (block_content | indentless_block_sequence)? // ********** * // | block_content | indentless_block_sequence // * // block_node ::= ALIAS // ***** // | properties block_content? // ********** * // | block_content // * // flow_node ::= ALIAS // ***** // | properties flow_content? // ********** * // | flow_content // * // properties ::= TAG ANCHOR? | ANCHOR TAG? // ************************* // block_content ::= block_collection | flow_collection | SCALAR // ****** // flow_content ::= flow_collection | SCALAR // ****** func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() token := peek_token(parser) if token == nil { return false } if token.typ == yaml_ALIAS_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_ALIAS_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, anchor: token.value, } skip_token(parser) return true } start_mark := token.start_mark end_mark := token.start_mark var tag_token bool var tag_handle, tag_suffix, anchor []byte var tag_mark yaml_mark_t if token.typ == yaml_ANCHOR_TOKEN { anchor = token.value start_mark = token.start_mark end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ == yaml_TAG_TOKEN { tag_token = true tag_handle = token.value tag_suffix = token.suffix tag_mark = token.start_mark end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } } } else if token.typ == yaml_TAG_TOKEN { tag_token = true tag_handle = token.value tag_suffix = token.suffix start_mark = token.start_mark tag_mark = token.start_mark end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ == yaml_ANCHOR_TOKEN { anchor = token.value end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } } } var tag []byte if tag_token { if len(tag_handle) == 0 { tag = tag_suffix tag_suffix = nil } else { for i := range parser.tag_directives { if bytes.Equal(parser.tag_directives[i].handle, tag_handle) { tag = append([]byte(nil), parser.tag_directives[i].prefix...) tag = append(tag, tag_suffix...) break } } if len(tag) == 0 { yaml_parser_set_parser_error_context(parser, "while parsing a node", start_mark, "found undefined tag handle", tag_mark) return false } } } implicit := len(tag) == 0 if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), } return true } if token.typ == yaml_SCALAR_TOKEN { var plain_implicit, quoted_implicit bool end_mark = token.end_mark if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') { plain_implicit = true } else if len(tag) == 0 { quoted_implicit = true } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, value: token.value, implicit: plain_implicit, quoted_implicit: quoted_implicit, style: yaml_style_t(token.style), } skip_token(parser) return true } if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN { // [Go] Some of the events below can be merged as they differ only on style. end_mark = token.end_mark parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), } return true } if token.typ == yaml_FLOW_MAPPING_START_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), } return true } if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), } return true } if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), } return true } if len(anchor) > 0 || len(tag) > 0 { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, quoted_implicit: false, style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), } return true } context := "while parsing a flow node" if block { context = "while parsing a block node" } yaml_parser_set_parser_error_context(parser, context, start_mark, "did not find expected node content", token.start_mark) return false } // Parse the productions: // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END // ******************** *********** * ********* // func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ == yaml_BLOCK_ENTRY_TOKEN { mark := token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE) return yaml_parser_parse_node(parser, event, true, false) } else { parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } } if token.typ == yaml_BLOCK_END_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) return true } context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a block collection", context_mark, "did not find expected '-' indicator", token.start_mark) } // Parse the productions: // indentless_sequence ::= (BLOCK-ENTRY block_node?)+ // *********** * func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_BLOCK_ENTRY_TOKEN { mark := token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_KEY_TOKEN && token.typ != yaml_VALUE_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE) return yaml_parser_parse_node(parser, event, true, false) } parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, start_mark: token.start_mark, end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark? } return true } // Parse the productions: // block_mapping ::= BLOCK-MAPPING_START // ******************* // ((KEY block_node_or_indentless_sequence?)? // *** * // (VALUE block_node_or_indentless_sequence?)?)* // // BLOCK-END // ********* // func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ == yaml_KEY_TOKEN { mark := token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_KEY_TOKEN && token.typ != yaml_VALUE_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE) return yaml_parser_parse_node(parser, event, true, true) } else { parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } } else if token.typ == yaml_BLOCK_END_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) return true } context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a block mapping", context_mark, "did not find expected key", token.start_mark) } // Parse the productions: // block_mapping ::= BLOCK-MAPPING_START // // ((KEY block_node_or_indentless_sequence?)? // // (VALUE block_node_or_indentless_sequence?)?)* // ***** * // BLOCK-END // // func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_VALUE_TOKEN { mark := token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_KEY_TOKEN && token.typ != yaml_VALUE_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE) return yaml_parser_parse_node(parser, event, true, true) } parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } // Parse the productions: // flow_sequence ::= FLOW-SEQUENCE-START // ******************* // (flow_sequence_entry FLOW-ENTRY)* // * ********** // flow_sequence_entry? // * // FLOW-SEQUENCE-END // ***************** // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * // func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { if !first { if token.typ == yaml_FLOW_ENTRY_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } } else { context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a flow sequence", context_mark, "did not find expected ',' or ']'", token.start_mark) } } if token.typ == yaml_KEY_TOKEN { parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, implicit: true, style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), } skip_token(parser) return true } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) return true } // // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // *** * // func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ != yaml_VALUE_TOKEN && token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE) return yaml_parser_parse_node(parser, event, false, false) } mark := token.end_mark skip_token(parser) parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // ***** * // func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_VALUE_TOKEN { skip_token(parser) token := peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * // func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, start_mark: token.start_mark, end_mark: token.start_mark, // [Go] Shouldn't this be end_mark? } return true } // Parse the productions: // flow_mapping ::= FLOW-MAPPING-START // ****************** // (flow_mapping_entry FLOW-ENTRY)* // * ********** // flow_mapping_entry? // ****************** // FLOW-MAPPING-END // **************** // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * *** * // func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_MAPPING_END_TOKEN { if !first { if token.typ == yaml_FLOW_ENTRY_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } } else { context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a flow mapping", context_mark, "did not find expected ',' or '}'", token.start_mark) } } if token.typ == yaml_KEY_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_VALUE_TOKEN && token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE) return yaml_parser_parse_node(parser, event, false, false) } else { parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) return true } // Parse the productions: // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * ***** * // func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { token := peek_token(parser) if token == nil { return false } if empty { parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } if token.typ == yaml_VALUE_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } // Generate an empty scalar event. func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool { *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, start_mark: mark, end_mark: mark, value: nil, // Empty implicit: true, style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), } return true } var default_tag_directives = []yaml_tag_directive_t{ {[]byte("!"), []byte("!")}, {[]byte("!!"), []byte("tag:yaml.org,2002:")}, } // Parse directives. func yaml_parser_process_directives(parser *yaml_parser_t, version_directive_ref **yaml_version_directive_t, tag_directives_ref *[]yaml_tag_directive_t) bool { var version_directive *yaml_version_directive_t var tag_directives []yaml_tag_directive_t token := peek_token(parser) if token == nil { return false } for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN { if token.typ == yaml_VERSION_DIRECTIVE_TOKEN { if version_directive != nil { yaml_parser_set_parser_error(parser, "found duplicate %YAML directive", token.start_mark) return false } if token.major != 1 || token.minor != 1 { yaml_parser_set_parser_error(parser, "found incompatible YAML document", token.start_mark) return false } version_directive = &yaml_version_directive_t{ major: token.major, minor: token.minor, } } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN { value := yaml_tag_directive_t{ handle: token.value, prefix: token.prefix, } if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) { return false } tag_directives = append(tag_directives, value) } skip_token(parser) token = peek_token(parser) if token == nil { return false } } for i := range default_tag_directives { if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) { return false } } if version_directive_ref != nil { *version_directive_ref = version_directive } if tag_directives_ref != nil { *tag_directives_ref = tag_directives } return true } // Append a tag directive to the directives stack. func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool { for i := range parser.tag_directives { if bytes.Equal(value.handle, parser.tag_directives[i].handle) { if allow_duplicates { return true } return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark) } } // [Go] I suspect the copy is unnecessary. This was likely done // because there was no way to track ownership of the data. value_copy := yaml_tag_directive_t{ handle: make([]byte, len(value.handle)), prefix: make([]byte, len(value.prefix)), } copy(value_copy.handle, value.handle) copy(value_copy.prefix, value.prefix) parser.tag_directives = append(parser.tag_directives, value_copy) return true } ================================================ FILE: vendor/gopkg.in/yaml.v2/readerc.go ================================================ package yaml import ( "io" ) // Set the reader error and return 0. func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { parser.error = yaml_READER_ERROR parser.problem = problem parser.problem_offset = offset parser.problem_value = value return false } // Byte order marks. const ( bom_UTF8 = "\xef\xbb\xbf" bom_UTF16LE = "\xff\xfe" bom_UTF16BE = "\xfe\xff" ) // Determine the input stream encoding by checking the BOM symbol. If no BOM is // found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { // Ensure that we had enough bytes in the raw buffer. for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { if !yaml_parser_update_raw_buffer(parser) { return false } } // Determine the encoding. buf := parser.raw_buffer pos := parser.raw_buffer_pos avail := len(buf) - pos if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { parser.encoding = yaml_UTF16LE_ENCODING parser.raw_buffer_pos += 2 parser.offset += 2 } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { parser.encoding = yaml_UTF16BE_ENCODING parser.raw_buffer_pos += 2 parser.offset += 2 } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { parser.encoding = yaml_UTF8_ENCODING parser.raw_buffer_pos += 3 parser.offset += 3 } else { parser.encoding = yaml_UTF8_ENCODING } return true } // Update the raw buffer. func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { size_read := 0 // Return if the raw buffer is full. if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { return true } // Return on EOF. if parser.eof { return true } // Move the remaining bytes in the raw buffer to the beginning. if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) } parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] parser.raw_buffer_pos = 0 // Call the read handler to fill the buffer. size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] if err == io.EOF { parser.eof = true } else if err != nil { return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) } return true } // Ensure that the buffer contains at least `length` characters. // Return true on success, false on failure. // // The length is supposed to be significantly less that the buffer size. func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { if parser.read_handler == nil { panic("read handler must be set") } // If the EOF flag is set and the raw buffer is empty, do nothing. if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { return true } // Return if the buffer contains enough characters. if parser.unread >= length { return true } // Determine the input encoding if it is not known yet. if parser.encoding == yaml_ANY_ENCODING { if !yaml_parser_determine_encoding(parser) { return false } } // Move the unread characters to the beginning of the buffer. buffer_len := len(parser.buffer) if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { copy(parser.buffer, parser.buffer[parser.buffer_pos:]) buffer_len -= parser.buffer_pos parser.buffer_pos = 0 } else if parser.buffer_pos == buffer_len { buffer_len = 0 parser.buffer_pos = 0 } // Open the whole buffer for writing, and cut it before returning. parser.buffer = parser.buffer[:cap(parser.buffer)] // Fill the buffer until it has enough characters. first := true for parser.unread < length { // Fill the raw buffer if necessary. if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { if !yaml_parser_update_raw_buffer(parser) { parser.buffer = parser.buffer[:buffer_len] return false } } first = false // Decode the raw buffer. inner: for parser.raw_buffer_pos != len(parser.raw_buffer) { var value rune var width int raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos // Decode the next character. switch parser.encoding { case yaml_UTF8_ENCODING: // Decode a UTF-8 character. Check RFC 3629 // (http://www.ietf.org/rfc/rfc3629.txt) for more details. // // The following table (taken from the RFC) is used for // decoding. // // Char. number range | UTF-8 octet sequence // (hexadecimal) | (binary) // --------------------+------------------------------------ // 0000 0000-0000 007F | 0xxxxxxx // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx // // Additionally, the characters in the range 0xD800-0xDFFF // are prohibited as they are reserved for use with UTF-16 // surrogate pairs. // Determine the length of the UTF-8 sequence. octet := parser.raw_buffer[parser.raw_buffer_pos] switch { case octet&0x80 == 0x00: width = 1 case octet&0xE0 == 0xC0: width = 2 case octet&0xF0 == 0xE0: width = 3 case octet&0xF8 == 0xF0: width = 4 default: // The leading octet is invalid. return yaml_parser_set_reader_error(parser, "invalid leading UTF-8 octet", parser.offset, int(octet)) } // Check if the raw buffer contains an incomplete character. if width > raw_unread { if parser.eof { return yaml_parser_set_reader_error(parser, "incomplete UTF-8 octet sequence", parser.offset, -1) } break inner } // Decode the leading octet. switch { case octet&0x80 == 0x00: value = rune(octet & 0x7F) case octet&0xE0 == 0xC0: value = rune(octet & 0x1F) case octet&0xF0 == 0xE0: value = rune(octet & 0x0F) case octet&0xF8 == 0xF0: value = rune(octet & 0x07) default: value = 0 } // Check and decode the trailing octets. for k := 1; k < width; k++ { octet = parser.raw_buffer[parser.raw_buffer_pos+k] // Check if the octet is valid. if (octet & 0xC0) != 0x80 { return yaml_parser_set_reader_error(parser, "invalid trailing UTF-8 octet", parser.offset+k, int(octet)) } // Decode the octet. value = (value << 6) + rune(octet&0x3F) } // Check the length of the sequence against the value. switch { case width == 1: case width == 2 && value >= 0x80: case width == 3 && value >= 0x800: case width == 4 && value >= 0x10000: default: return yaml_parser_set_reader_error(parser, "invalid length of a UTF-8 sequence", parser.offset, -1) } // Check the range of the value. if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { return yaml_parser_set_reader_error(parser, "invalid Unicode character", parser.offset, int(value)) } case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: var low, high int if parser.encoding == yaml_UTF16LE_ENCODING { low, high = 0, 1 } else { low, high = 1, 0 } // The UTF-16 encoding is not as simple as one might // naively think. Check RFC 2781 // (http://www.ietf.org/rfc/rfc2781.txt). // // Normally, two subsequent bytes describe a Unicode // character. However a special technique (called a // surrogate pair) is used for specifying character // values larger than 0xFFFF. // // A surrogate pair consists of two pseudo-characters: // high surrogate area (0xD800-0xDBFF) // low surrogate area (0xDC00-0xDFFF) // // The following formulas are used for decoding // and encoding characters using surrogate pairs: // // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) // W1 = 110110yyyyyyyyyy // W2 = 110111xxxxxxxxxx // // where U is the character value, W1 is the high surrogate // area, W2 is the low surrogate area. // Check for incomplete UTF-16 character. if raw_unread < 2 { if parser.eof { return yaml_parser_set_reader_error(parser, "incomplete UTF-16 character", parser.offset, -1) } break inner } // Get the character. value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) // Check for unexpected low surrogate area. if value&0xFC00 == 0xDC00 { return yaml_parser_set_reader_error(parser, "unexpected low surrogate area", parser.offset, int(value)) } // Check for a high surrogate area. if value&0xFC00 == 0xD800 { width = 4 // Check for incomplete surrogate pair. if raw_unread < 4 { if parser.eof { return yaml_parser_set_reader_error(parser, "incomplete UTF-16 surrogate pair", parser.offset, -1) } break inner } // Get the next character. value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) // Check for a low surrogate area. if value2&0xFC00 != 0xDC00 { return yaml_parser_set_reader_error(parser, "expected low surrogate area", parser.offset+2, int(value2)) } // Generate the value of the surrogate pair. value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) } else { width = 2 } default: panic("impossible") } // Check if the character is in the allowed range: // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) // | [#x10000-#x10FFFF] (32 bit) switch { case value == 0x09: case value == 0x0A: case value == 0x0D: case value >= 0x20 && value <= 0x7E: case value == 0x85: case value >= 0xA0 && value <= 0xD7FF: case value >= 0xE000 && value <= 0xFFFD: case value >= 0x10000 && value <= 0x10FFFF: default: return yaml_parser_set_reader_error(parser, "control characters are not allowed", parser.offset, int(value)) } // Move the raw pointers. parser.raw_buffer_pos += width parser.offset += width // Finally put the character into the buffer. if value <= 0x7F { // 0000 0000-0000 007F . 0xxxxxxx parser.buffer[buffer_len+0] = byte(value) buffer_len += 1 } else if value <= 0x7FF { // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) buffer_len += 2 } else if value <= 0xFFFF { // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) buffer_len += 3 } else { // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) buffer_len += 4 } parser.unread++ } // On EOF, put NUL into the buffer and return. if parser.eof { parser.buffer[buffer_len] = 0 buffer_len++ parser.unread++ break } } parser.buffer = parser.buffer[:buffer_len] return true } ================================================ FILE: vendor/gopkg.in/yaml.v2/resolve.go ================================================ package yaml import ( "encoding/base64" "math" "regexp" "strconv" "strings" "unicode/utf8" ) type resolveMapItem struct { value interface{} tag string } var resolveTable = make([]byte, 256) var resolveMap = make(map[string]resolveMapItem) func init() { t := resolveTable t[int('+')] = 'S' // Sign t[int('-')] = 'S' for _, c := range "0123456789" { t[int(c)] = 'D' // Digit } for _, c := range "yYnNtTfFoO~" { t[int(c)] = 'M' // In map } t[int('.')] = '.' // Float (potentially in map) var resolveMapList = []struct { v interface{} tag string l []string }{ {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, {"<<", yaml_MERGE_TAG, []string{"<<"}}, } m := resolveMap for _, item := range resolveMapList { for _, s := range item.l { m[s] = resolveMapItem{item.v, item.tag} } } } const longTagPrefix = "tag:yaml.org,2002:" func shortTag(tag string) string { // TODO This can easily be made faster and produce less garbage. if strings.HasPrefix(tag, longTagPrefix) { return "!!" + tag[len(longTagPrefix):] } return tag } func longTag(tag string) string { if strings.HasPrefix(tag, "!!") { return longTagPrefix + tag[2:] } return tag } func resolvableTag(tag string) bool { switch tag { case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG: return true } return false } var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`) func resolve(tag string, in string) (rtag string, out interface{}) { if !resolvableTag(tag) { return tag, in } defer func() { switch tag { case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: return } failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) }() // Any data is accepted as a !!str or !!binary. // Otherwise, the prefix is enough of a hint about what it might be. hint := byte('N') if in != "" { hint = resolveTable[in[0]] } if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { // Handle things we can lookup in a map. if item, ok := resolveMap[in]; ok { return item.tag, item.value } // Base 60 floats are a bad idea, were dropped in YAML 1.2, and // are purposefully unsupported here. They're still quoted on // the way out for compatibility with other parser, though. switch hint { case 'M': // We've already checked the map above. case '.': // Not in the map, so maybe a normal float. floatv, err := strconv.ParseFloat(in, 64) if err == nil { return yaml_FLOAT_TAG, floatv } case 'D', 'S': // Int, float, or timestamp. plain := strings.Replace(in, "_", "", -1) intv, err := strconv.ParseInt(plain, 0, 64) if err == nil { if intv == int64(int(intv)) { return yaml_INT_TAG, int(intv) } else { return yaml_INT_TAG, intv } } uintv, err := strconv.ParseUint(plain, 0, 64) if err == nil { return yaml_INT_TAG, uintv } if yamlStyleFloat.MatchString(plain) { floatv, err := strconv.ParseFloat(plain, 64) if err == nil { return yaml_FLOAT_TAG, floatv } } if strings.HasPrefix(plain, "0b") { intv, err := strconv.ParseInt(plain[2:], 2, 64) if err == nil { if intv == int64(int(intv)) { return yaml_INT_TAG, int(intv) } else { return yaml_INT_TAG, intv } } uintv, err := strconv.ParseUint(plain[2:], 2, 64) if err == nil { return yaml_INT_TAG, uintv } } else if strings.HasPrefix(plain, "-0b") { intv, err := strconv.ParseInt(plain[3:], 2, 64) if err == nil { if intv == int64(int(intv)) { return yaml_INT_TAG, -int(intv) } else { return yaml_INT_TAG, -intv } } } // XXX Handle timestamps here. default: panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") } } if tag == yaml_BINARY_TAG { return yaml_BINARY_TAG, in } if utf8.ValidString(in) { return yaml_STR_TAG, in } return yaml_BINARY_TAG, encodeBase64(in) } // encodeBase64 encodes s as base64 that is broken up into multiple lines // as appropriate for the resulting length. func encodeBase64(s string) string { const lineLen = 70 encLen := base64.StdEncoding.EncodedLen(len(s)) lines := encLen/lineLen + 1 buf := make([]byte, encLen*2+lines) in := buf[0:encLen] out := buf[encLen:] base64.StdEncoding.Encode(in, []byte(s)) k := 0 for i := 0; i < len(in); i += lineLen { j := i + lineLen if j > len(in) { j = len(in) } k += copy(out[k:], in[i:j]) if lines > 1 { out[k] = '\n' k++ } } return string(out[:k]) } ================================================ FILE: vendor/gopkg.in/yaml.v2/scannerc.go ================================================ package yaml import ( "bytes" "fmt" ) // Introduction // ************ // // The following notes assume that you are familiar with the YAML specification // (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in // some cases we are less restrictive that it requires. // // The process of transforming a YAML stream into a sequence of events is // divided on two steps: Scanning and Parsing. // // The Scanner transforms the input stream into a sequence of tokens, while the // parser transform the sequence of tokens produced by the Scanner into a // sequence of parsing events. // // The Scanner is rather clever and complicated. The Parser, on the contrary, // is a straightforward implementation of a recursive-descendant parser (or, // LL(1) parser, as it is usually called). // // Actually there are two issues of Scanning that might be called "clever", the // rest is quite straightforward. The issues are "block collection start" and // "simple keys". Both issues are explained below in details. // // Here the Scanning step is explained and implemented. We start with the list // of all the tokens produced by the Scanner together with short descriptions. // // Now, tokens: // // STREAM-START(encoding) # The stream start. // STREAM-END # The stream end. // VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. // TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. // DOCUMENT-START # '---' // DOCUMENT-END # '...' // BLOCK-SEQUENCE-START # Indentation increase denoting a block // BLOCK-MAPPING-START # sequence or a block mapping. // BLOCK-END # Indentation decrease. // FLOW-SEQUENCE-START # '[' // FLOW-SEQUENCE-END # ']' // BLOCK-SEQUENCE-START # '{' // BLOCK-SEQUENCE-END # '}' // BLOCK-ENTRY # '-' // FLOW-ENTRY # ',' // KEY # '?' or nothing (simple keys). // VALUE # ':' // ALIAS(anchor) # '*anchor' // ANCHOR(anchor) # '&anchor' // TAG(handle,suffix) # '!handle!suffix' // SCALAR(value,style) # A scalar. // // The following two tokens are "virtual" tokens denoting the beginning and the // end of the stream: // // STREAM-START(encoding) // STREAM-END // // We pass the information about the input stream encoding with the // STREAM-START token. // // The next two tokens are responsible for tags: // // VERSION-DIRECTIVE(major,minor) // TAG-DIRECTIVE(handle,prefix) // // Example: // // %YAML 1.1 // %TAG ! !foo // %TAG !yaml! tag:yaml.org,2002: // --- // // The correspoding sequence of tokens: // // STREAM-START(utf-8) // VERSION-DIRECTIVE(1,1) // TAG-DIRECTIVE("!","!foo") // TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") // DOCUMENT-START // STREAM-END // // Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole // line. // // The document start and end indicators are represented by: // // DOCUMENT-START // DOCUMENT-END // // Note that if a YAML stream contains an implicit document (without '---' // and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be // produced. // // In the following examples, we present whole documents together with the // produced tokens. // // 1. An implicit document: // // 'a scalar' // // Tokens: // // STREAM-START(utf-8) // SCALAR("a scalar",single-quoted) // STREAM-END // // 2. An explicit document: // // --- // 'a scalar' // ... // // Tokens: // // STREAM-START(utf-8) // DOCUMENT-START // SCALAR("a scalar",single-quoted) // DOCUMENT-END // STREAM-END // // 3. Several documents in a stream: // // 'a scalar' // --- // 'another scalar' // --- // 'yet another scalar' // // Tokens: // // STREAM-START(utf-8) // SCALAR("a scalar",single-quoted) // DOCUMENT-START // SCALAR("another scalar",single-quoted) // DOCUMENT-START // SCALAR("yet another scalar",single-quoted) // STREAM-END // // We have already introduced the SCALAR token above. The following tokens are // used to describe aliases, anchors, tag, and scalars: // // ALIAS(anchor) // ANCHOR(anchor) // TAG(handle,suffix) // SCALAR(value,style) // // The following series of examples illustrate the usage of these tokens: // // 1. A recursive sequence: // // &A [ *A ] // // Tokens: // // STREAM-START(utf-8) // ANCHOR("A") // FLOW-SEQUENCE-START // ALIAS("A") // FLOW-SEQUENCE-END // STREAM-END // // 2. A tagged scalar: // // !!float "3.14" # A good approximation. // // Tokens: // // STREAM-START(utf-8) // TAG("!!","float") // SCALAR("3.14",double-quoted) // STREAM-END // // 3. Various scalar styles: // // --- # Implicit empty plain scalars do not produce tokens. // --- a plain scalar // --- 'a single-quoted scalar' // --- "a double-quoted scalar" // --- |- // a literal scalar // --- >- // a folded // scalar // // Tokens: // // STREAM-START(utf-8) // DOCUMENT-START // DOCUMENT-START // SCALAR("a plain scalar",plain) // DOCUMENT-START // SCALAR("a single-quoted scalar",single-quoted) // DOCUMENT-START // SCALAR("a double-quoted scalar",double-quoted) // DOCUMENT-START // SCALAR("a literal scalar",literal) // DOCUMENT-START // SCALAR("a folded scalar",folded) // STREAM-END // // Now it's time to review collection-related tokens. We will start with // flow collections: // // FLOW-SEQUENCE-START // FLOW-SEQUENCE-END // FLOW-MAPPING-START // FLOW-MAPPING-END // FLOW-ENTRY // KEY // VALUE // // The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and // FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' // correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the // indicators '?' and ':', which are used for denoting mapping keys and values, // are represented by the KEY and VALUE tokens. // // The following examples show flow collections: // // 1. A flow sequence: // // [item 1, item 2, item 3] // // Tokens: // // STREAM-START(utf-8) // FLOW-SEQUENCE-START // SCALAR("item 1",plain) // FLOW-ENTRY // SCALAR("item 2",plain) // FLOW-ENTRY // SCALAR("item 3",plain) // FLOW-SEQUENCE-END // STREAM-END // // 2. A flow mapping: // // { // a simple key: a value, # Note that the KEY token is produced. // ? a complex key: another value, // } // // Tokens: // // STREAM-START(utf-8) // FLOW-MAPPING-START // KEY // SCALAR("a simple key",plain) // VALUE // SCALAR("a value",plain) // FLOW-ENTRY // KEY // SCALAR("a complex key",plain) // VALUE // SCALAR("another value",plain) // FLOW-ENTRY // FLOW-MAPPING-END // STREAM-END // // A simple key is a key which is not denoted by the '?' indicator. Note that // the Scanner still produce the KEY token whenever it encounters a simple key. // // For scanning block collections, the following tokens are used (note that we // repeat KEY and VALUE here): // // BLOCK-SEQUENCE-START // BLOCK-MAPPING-START // BLOCK-END // BLOCK-ENTRY // KEY // VALUE // // The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation // increase that precedes a block collection (cf. the INDENT token in Python). // The token BLOCK-END denote indentation decrease that ends a block collection // (cf. the DEDENT token in Python). However YAML has some syntax pecularities // that makes detections of these tokens more complex. // // The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators // '-', '?', and ':' correspondingly. // // The following examples show how the tokens BLOCK-SEQUENCE-START, // BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: // // 1. Block sequences: // // - item 1 // - item 2 // - // - item 3.1 // - item 3.2 // - // key 1: value 1 // key 2: value 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-ENTRY // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 3.1",plain) // BLOCK-ENTRY // SCALAR("item 3.2",plain) // BLOCK-END // BLOCK-ENTRY // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // BLOCK-END // STREAM-END // // 2. Block mappings: // // a simple key: a value # The KEY token is produced here. // ? a complex key // : another value // a mapping: // key 1: value 1 // key 2: value 2 // a sequence: // - item 1 // - item 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-MAPPING-START // KEY // SCALAR("a simple key",plain) // VALUE // SCALAR("a value",plain) // KEY // SCALAR("a complex key",plain) // VALUE // SCALAR("another value",plain) // KEY // SCALAR("a mapping",plain) // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // KEY // SCALAR("a sequence",plain) // VALUE // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // BLOCK-END // STREAM-END // // YAML does not always require to start a new block collection from a new // line. If the current line contains only '-', '?', and ':' indicators, a new // block collection may start at the current line. The following examples // illustrate this case: // // 1. Collections in a sequence: // // - - item 1 // - item 2 // - key 1: value 1 // key 2: value 2 // - ? complex key // : complex value // // Tokens: // // STREAM-START(utf-8) // BLOCK-SEQUENCE-START // BLOCK-ENTRY // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // BLOCK-ENTRY // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // BLOCK-ENTRY // BLOCK-MAPPING-START // KEY // SCALAR("complex key") // VALUE // SCALAR("complex value") // BLOCK-END // BLOCK-END // STREAM-END // // 2. Collections in a mapping: // // ? a sequence // : - item 1 // - item 2 // ? a mapping // : key 1: value 1 // key 2: value 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-MAPPING-START // KEY // SCALAR("a sequence",plain) // VALUE // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // KEY // SCALAR("a mapping",plain) // VALUE // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // BLOCK-END // STREAM-END // // YAML also permits non-indented sequences if they are included into a block // mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: // // key: // - item 1 # BLOCK-SEQUENCE-START is NOT produced here. // - item 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-MAPPING-START // KEY // SCALAR("key",plain) // VALUE // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // // Ensure that the buffer contains the required number of characters. // Return true on success, false on failure (reader error or memory error). func cache(parser *yaml_parser_t, length int) bool { // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B) return parser.unread >= length || yaml_parser_update_buffer(parser, length) } // Advance the buffer pointer. func skip(parser *yaml_parser_t) { parser.mark.index++ parser.mark.column++ parser.unread-- parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) } func skip_line(parser *yaml_parser_t) { if is_crlf(parser.buffer, parser.buffer_pos) { parser.mark.index += 2 parser.mark.column = 0 parser.mark.line++ parser.unread -= 2 parser.buffer_pos += 2 } else if is_break(parser.buffer, parser.buffer_pos) { parser.mark.index++ parser.mark.column = 0 parser.mark.line++ parser.unread-- parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) } } // Copy a character to a string buffer and advance pointers. func read(parser *yaml_parser_t, s []byte) []byte { w := width(parser.buffer[parser.buffer_pos]) if w == 0 { panic("invalid character sequence") } if len(s) == 0 { s = make([]byte, 0, 32) } if w == 1 && len(s)+w <= cap(s) { s = s[:len(s)+1] s[len(s)-1] = parser.buffer[parser.buffer_pos] parser.buffer_pos++ } else { s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...) parser.buffer_pos += w } parser.mark.index++ parser.mark.column++ parser.unread-- return s } // Copy a line break character to a string buffer and advance pointers. func read_line(parser *yaml_parser_t, s []byte) []byte { buf := parser.buffer pos := parser.buffer_pos switch { case buf[pos] == '\r' && buf[pos+1] == '\n': // CR LF . LF s = append(s, '\n') parser.buffer_pos += 2 parser.mark.index++ parser.unread-- case buf[pos] == '\r' || buf[pos] == '\n': // CR|LF . LF s = append(s, '\n') parser.buffer_pos += 1 case buf[pos] == '\xC2' && buf[pos+1] == '\x85': // NEL . LF s = append(s, '\n') parser.buffer_pos += 2 case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'): // LS|PS . LS|PS s = append(s, buf[parser.buffer_pos:pos+3]...) parser.buffer_pos += 3 default: return s } parser.mark.index++ parser.mark.column = 0 parser.mark.line++ parser.unread-- return s } // Get the next token. func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool { // Erase the token object. *token = yaml_token_t{} // [Go] Is this necessary? // No tokens after STREAM-END or error. if parser.stream_end_produced || parser.error != yaml_NO_ERROR { return true } // Ensure that the tokens queue contains enough tokens. if !parser.token_available { if !yaml_parser_fetch_more_tokens(parser) { return false } } // Fetch the next token from the queue. *token = parser.tokens[parser.tokens_head] parser.tokens_head++ parser.tokens_parsed++ parser.token_available = false if token.typ == yaml_STREAM_END_TOKEN { parser.stream_end_produced = true } return true } // Set the scanner error and return false. func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool { parser.error = yaml_SCANNER_ERROR parser.context = context parser.context_mark = context_mark parser.problem = problem parser.problem_mark = parser.mark return false } func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool { context := "while parsing a tag" if directive { context = "while parsing a %TAG directive" } return yaml_parser_set_scanner_error(parser, context, context_mark, problem) } func trace(args ...interface{}) func() { pargs := append([]interface{}{"+++"}, args...) fmt.Println(pargs...) pargs = append([]interface{}{"---"}, args...) return func() { fmt.Println(pargs...) } } // Ensure that the tokens queue contains at least one token which can be // returned to the Parser. func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { // While we need more tokens to fetch, do it. for { // Check if we really need to fetch more tokens. need_more_tokens := false if parser.tokens_head == len(parser.tokens) { // Queue is empty. need_more_tokens = true } else { // Check if any potential simple key may occupy the head position. if !yaml_parser_stale_simple_keys(parser) { return false } for i := range parser.simple_keys { simple_key := &parser.simple_keys[i] if simple_key.possible && simple_key.token_number == parser.tokens_parsed { need_more_tokens = true break } } } // We are finished. if !need_more_tokens { break } // Fetch the next token. if !yaml_parser_fetch_next_token(parser) { return false } } parser.token_available = true return true } // The dispatcher for token fetchers. func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { // Ensure that the buffer is initialized. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } // Check if we just started scanning. Fetch STREAM-START then. if !parser.stream_start_produced { return yaml_parser_fetch_stream_start(parser) } // Eat whitespaces and comments until we reach the next token. if !yaml_parser_scan_to_next_token(parser) { return false } // Remove obsolete potential simple keys. if !yaml_parser_stale_simple_keys(parser) { return false } // Check the indentation level against the current column. if !yaml_parser_unroll_indent(parser, parser.mark.column) { return false } // Ensure that the buffer contains at least 4 characters. 4 is the length // of the longest indicators ('--- ' and '... '). if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { return false } // Is it the end of the stream? if is_z(parser.buffer, parser.buffer_pos) { return yaml_parser_fetch_stream_end(parser) } // Is it a directive? if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' { return yaml_parser_fetch_directive(parser) } buf := parser.buffer pos := parser.buffer_pos // Is it the document start indicator? if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) { return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN) } // Is it the document end indicator? if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) { return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) } // Is it the flow sequence start indicator? if buf[pos] == '[' { return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) } // Is it the flow mapping start indicator? if parser.buffer[parser.buffer_pos] == '{' { return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN) } // Is it the flow sequence end indicator? if parser.buffer[parser.buffer_pos] == ']' { return yaml_parser_fetch_flow_collection_end(parser, yaml_FLOW_SEQUENCE_END_TOKEN) } // Is it the flow mapping end indicator? if parser.buffer[parser.buffer_pos] == '}' { return yaml_parser_fetch_flow_collection_end(parser, yaml_FLOW_MAPPING_END_TOKEN) } // Is it the flow entry indicator? if parser.buffer[parser.buffer_pos] == ',' { return yaml_parser_fetch_flow_entry(parser) } // Is it the block entry indicator? if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) { return yaml_parser_fetch_block_entry(parser) } // Is it the key indicator? if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { return yaml_parser_fetch_key(parser) } // Is it the value indicator? if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { return yaml_parser_fetch_value(parser) } // Is it an alias? if parser.buffer[parser.buffer_pos] == '*' { return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN) } // Is it an anchor? if parser.buffer[parser.buffer_pos] == '&' { return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN) } // Is it a tag? if parser.buffer[parser.buffer_pos] == '!' { return yaml_parser_fetch_tag(parser) } // Is it a literal scalar? if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 { return yaml_parser_fetch_block_scalar(parser, true) } // Is it a folded scalar? if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 { return yaml_parser_fetch_block_scalar(parser, false) } // Is it a single-quoted scalar? if parser.buffer[parser.buffer_pos] == '\'' { return yaml_parser_fetch_flow_scalar(parser, true) } // Is it a double-quoted scalar? if parser.buffer[parser.buffer_pos] == '"' { return yaml_parser_fetch_flow_scalar(parser, false) } // Is it a plain scalar? // // A plain scalar may start with any non-blank characters except // // '-', '?', ':', ',', '[', ']', '{', '}', // '#', '&', '*', '!', '|', '>', '\'', '\"', // '%', '@', '`'. // // In the block context (and, for the '-' indicator, in the flow context // too), it may also start with the characters // // '-', '?', ':' // // if it is followed by a non-space character. // // The last rule is more restrictive than the specification requires. // [Go] Make this logic more reasonable. //switch parser.buffer[parser.buffer_pos] { //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': //} if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' || parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' || parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' || parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') || (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) || (parser.flow_level == 0 && (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') && !is_blankz(parser.buffer, parser.buffer_pos+1)) { return yaml_parser_fetch_plain_scalar(parser) } // If we don't determine the token type so far, it is an error. return yaml_parser_set_scanner_error(parser, "while scanning for the next token", parser.mark, "found character that cannot start any token") } // Check the list of potential simple keys and remove the positions that // cannot contain simple keys anymore. func yaml_parser_stale_simple_keys(parser *yaml_parser_t) bool { // Check for a potential simple key for each flow level. for i := range parser.simple_keys { simple_key := &parser.simple_keys[i] // The specification requires that a simple key // // - is limited to a single line, // - is shorter than 1024 characters. if simple_key.possible && (simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index) { // Check if the potential simple key to be removed is required. if simple_key.required { return yaml_parser_set_scanner_error(parser, "while scanning a simple key", simple_key.mark, "could not find expected ':'") } simple_key.possible = false } } return true } // Check if a simple key may start at the current position and add it if // needed. func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { // A simple key is required at the current position if the scanner is in // the block context and the current column coincides with the indentation // level. required := parser.flow_level == 0 && parser.indent == parser.mark.column // A simple key is required only when it is the first token in the current // line. Therefore it is always allowed. But we add a check anyway. if required && !parser.simple_key_allowed { panic("should not happen") } // // If the current position may start a simple key, save it. // if parser.simple_key_allowed { simple_key := yaml_simple_key_t{ possible: true, required: required, token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), } simple_key.mark = parser.mark if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_keys[len(parser.simple_keys)-1] = simple_key } return true } // Remove a potential simple key at the current flow level. func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { i := len(parser.simple_keys) - 1 if parser.simple_keys[i].possible { // If the key is required, it is an error. if parser.simple_keys[i].required { return yaml_parser_set_scanner_error(parser, "while scanning a simple key", parser.simple_keys[i].mark, "could not find expected ':'") } } // Remove the key from the stack. parser.simple_keys[i].possible = false return true } // Increase the flow level and resize the simple key list if needed. func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { // Reset the simple key on the next level. parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) // Increase the flow level. parser.flow_level++ return true } // Decrease the flow level. func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { if parser.flow_level > 0 { parser.flow_level-- parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1] } return true } // Push the current indentation level to the stack and set the new level // the current column is greater than the indentation level. In this case, // append or insert the specified token into the token queue. func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool { // In the flow context, do nothing. if parser.flow_level > 0 { return true } if parser.indent < column { // Push the current indentation level to the stack and set the new // indentation level. parser.indents = append(parser.indents, parser.indent) parser.indent = column // Create a token and insert it into the queue. token := yaml_token_t{ typ: typ, start_mark: mark, end_mark: mark, } if number > -1 { number -= parser.tokens_parsed } yaml_insert_token(parser, number, &token) } return true } // Pop indentation levels from the indents stack until the current level // becomes less or equal to the column. For each indentation level, append // the BLOCK-END token. func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool { // In the flow context, do nothing. if parser.flow_level > 0 { return true } // Loop through the indentation levels in the stack. for parser.indent > column { // Create a token and append it to the queue. token := yaml_token_t{ typ: yaml_BLOCK_END_TOKEN, start_mark: parser.mark, end_mark: parser.mark, } yaml_insert_token(parser, -1, &token) // Pop the indentation level. parser.indent = parser.indents[len(parser.indents)-1] parser.indents = parser.indents[:len(parser.indents)-1] } return true } // Initialize the scanner and produce the STREAM-START token. func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { // Set the initial indentation. parser.indent = -1 // Initialize the simple key stack. parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) // A simple key is allowed at the beginning of the stream. parser.simple_key_allowed = true // We have started. parser.stream_start_produced = true // Create the STREAM-START token and append it to the queue. token := yaml_token_t{ typ: yaml_STREAM_START_TOKEN, start_mark: parser.mark, end_mark: parser.mark, encoding: parser.encoding, } yaml_insert_token(parser, -1, &token) return true } // Produce the STREAM-END token and shut down the scanner. func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { // Force new line. if parser.mark.column != 0 { parser.mark.column = 0 parser.mark.line++ } // Reset the indentation level. if !yaml_parser_unroll_indent(parser, -1) { return false } // Reset simple keys. if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_key_allowed = false // Create the STREAM-END token and append it to the queue. token := yaml_token_t{ typ: yaml_STREAM_END_TOKEN, start_mark: parser.mark, end_mark: parser.mark, } yaml_insert_token(parser, -1, &token) return true } // Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { // Reset the indentation level. if !yaml_parser_unroll_indent(parser, -1) { return false } // Reset simple keys. if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_key_allowed = false // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. token := yaml_token_t{} if !yaml_parser_scan_directive(parser, &token) { return false } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the DOCUMENT-START or DOCUMENT-END token. func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { // Reset the indentation level. if !yaml_parser_unroll_indent(parser, -1) { return false } // Reset simple keys. if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_key_allowed = false // Consume the token. start_mark := parser.mark skip(parser) skip(parser) skip(parser) end_mark := parser.mark // Create the DOCUMENT-START or DOCUMENT-END token. token := yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { // The indicators '[' and '{' may start a simple key. if !yaml_parser_save_simple_key(parser) { return false } // Increase the flow level. if !yaml_parser_increase_flow_level(parser) { return false } // A simple key may follow the indicators '[' and '{'. parser.simple_key_allowed = true // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. token := yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool { // Reset any potential simple key on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Decrease the flow level. if !yaml_parser_decrease_flow_level(parser) { return false } // No simple keys after the indicators ']' and '}'. parser.simple_key_allowed = false // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. token := yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the FLOW-ENTRY token. func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool { // Reset any potential simple keys on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Simple keys are allowed after ','. parser.simple_key_allowed = true // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the FLOW-ENTRY token and append it to the queue. token := yaml_token_t{ typ: yaml_FLOW_ENTRY_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the BLOCK-ENTRY token. func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool { // Check if the scanner is in the block context. if parser.flow_level == 0 { // Check if we are allowed to start a new entry. if !parser.simple_key_allowed { return yaml_parser_set_scanner_error(parser, "", parser.mark, "block sequence entries are not allowed in this context") } // Add the BLOCK-SEQUENCE-START token if needed. if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) { return false } } else { // It is an error for the '-' indicator to occur in the flow context, // but we let the Parser detect and report about it because the Parser // is able to point to the context. } // Reset any potential simple keys on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Simple keys are allowed after '-'. parser.simple_key_allowed = true // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the BLOCK-ENTRY token and append it to the queue. token := yaml_token_t{ typ: yaml_BLOCK_ENTRY_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the KEY token. func yaml_parser_fetch_key(parser *yaml_parser_t) bool { // In the block context, additional checks are required. if parser.flow_level == 0 { // Check if we are allowed to start a new key (not nessesary simple). if !parser.simple_key_allowed { return yaml_parser_set_scanner_error(parser, "", parser.mark, "mapping keys are not allowed in this context") } // Add the BLOCK-MAPPING-START token if needed. if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { return false } } // Reset any potential simple keys on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Simple keys are allowed after '?' in the block context. parser.simple_key_allowed = parser.flow_level == 0 // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the KEY token and append it to the queue. token := yaml_token_t{ typ: yaml_KEY_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the VALUE token. func yaml_parser_fetch_value(parser *yaml_parser_t) bool { simple_key := &parser.simple_keys[len(parser.simple_keys)-1] // Have we found a simple key? if simple_key.possible { // Create the KEY token and insert it into the queue. token := yaml_token_t{ typ: yaml_KEY_TOKEN, start_mark: simple_key.mark, end_mark: simple_key.mark, } yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token) // In the block context, we may need to add the BLOCK-MAPPING-START token. if !yaml_parser_roll_indent(parser, simple_key.mark.column, simple_key.token_number, yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) { return false } // Remove the simple key. simple_key.possible = false // A simple key cannot follow another simple key. parser.simple_key_allowed = false } else { // The ':' indicator follows a complex key. // In the block context, extra checks are required. if parser.flow_level == 0 { // Check if we are allowed to start a complex value. if !parser.simple_key_allowed { return yaml_parser_set_scanner_error(parser, "", parser.mark, "mapping values are not allowed in this context") } // Add the BLOCK-MAPPING-START token if needed. if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { return false } } // Simple keys after ':' are allowed in the block context. parser.simple_key_allowed = parser.flow_level == 0 } // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the VALUE token and append it to the queue. token := yaml_token_t{ typ: yaml_VALUE_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the ALIAS or ANCHOR token. func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool { // An anchor or an alias could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow an anchor or an alias. parser.simple_key_allowed = false // Create the ALIAS or ANCHOR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_anchor(parser, &token, typ) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the TAG token. func yaml_parser_fetch_tag(parser *yaml_parser_t) bool { // A tag could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow a tag. parser.simple_key_allowed = false // Create the TAG token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_tag(parser, &token) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool { // Remove any potential simple keys. if !yaml_parser_remove_simple_key(parser) { return false } // A simple key may follow a block scalar. parser.simple_key_allowed = true // Create the SCALAR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_block_scalar(parser, &token, literal) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool { // A plain scalar could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow a flow scalar. parser.simple_key_allowed = false // Create the SCALAR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_flow_scalar(parser, &token, single) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the SCALAR(...,plain) token. func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { // A plain scalar could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow a flow scalar. parser.simple_key_allowed = false // Create the SCALAR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_plain_scalar(parser, &token) { return false } yaml_insert_token(parser, -1, &token) return true } // Eat whitespaces and comments until the next token is found. func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { // Until the next token is not found. for { // Allow the BOM mark to start a line. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) { skip(parser) } // Eat whitespaces. // Tabs are allowed: // - in the flow context // - in the block context, but not at the beginning of the line or // after '-', '?', or ':' (complex value). if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Eat a comment until a line break. if parser.buffer[parser.buffer_pos] == '#' { for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } } // If it is a line break, eat it. if is_break(parser.buffer, parser.buffer_pos) { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) // In the block context, a new line may start a simple key. if parser.flow_level == 0 { parser.simple_key_allowed = true } } else { break // We have found a token. } } return true } // Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. // // Scope: // %YAML 1.1 # a comment \n // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // %TAG !yaml! tag:yaml.org,2002: \n // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { // Eat '%'. start_mark := parser.mark skip(parser) // Scan the directive name. var name []byte if !yaml_parser_scan_directive_name(parser, start_mark, &name) { return false } // Is it a YAML directive? if bytes.Equal(name, []byte("YAML")) { // Scan the VERSION directive value. var major, minor int8 if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) { return false } end_mark := parser.mark // Create a VERSION-DIRECTIVE token. *token = yaml_token_t{ typ: yaml_VERSION_DIRECTIVE_TOKEN, start_mark: start_mark, end_mark: end_mark, major: major, minor: minor, } // Is it a TAG directive? } else if bytes.Equal(name, []byte("TAG")) { // Scan the TAG directive value. var handle, prefix []byte if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) { return false } end_mark := parser.mark // Create a TAG-DIRECTIVE token. *token = yaml_token_t{ typ: yaml_TAG_DIRECTIVE_TOKEN, start_mark: start_mark, end_mark: end_mark, value: handle, prefix: prefix, } // Unknown directive. } else { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "found unknown directive name") return false } // Eat the rest of the line including any comments. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } if parser.buffer[parser.buffer_pos] == '#' { for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } } // Check if we are at the end of the line. if !is_breakz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "did not find expected comment or line break") return false } // Eat a line break. if is_break(parser.buffer, parser.buffer_pos) { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) } return true } // Scan the directive name. // // Scope: // %YAML 1.1 # a comment \n // ^^^^ // %TAG !yaml! tag:yaml.org,2002: \n // ^^^ // func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { // Consume the directive name. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } var s []byte for is_alpha(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the name is empty. if len(s) == 0 { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "could not find expected directive name") return false } // Check for an blank character after the name. if !is_blankz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "found unexpected non-alphabetical character") return false } *name = s return true } // Scan the value of VERSION-DIRECTIVE. // // Scope: // %YAML 1.1 # a comment \n // ^^^^^^ func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { // Eat whitespaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Consume the major version number. if !yaml_parser_scan_version_directive_number(parser, start_mark, major) { return false } // Eat '.'. if parser.buffer[parser.buffer_pos] != '.' { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "did not find expected digit or '.' character") } skip(parser) // Consume the minor version number. if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) { return false } return true } const max_number_length = 2 // Scan the version number of VERSION-DIRECTIVE. // // Scope: // %YAML 1.1 # a comment \n // ^ // %YAML 1.1 # a comment \n // ^ func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { // Repeat while the next character is digit. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } var value, length int8 for is_digit(parser.buffer, parser.buffer_pos) { // Check if the number is too long. length++ if length > max_number_length { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "found extremely long version number") } value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos)) skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the number was present. if length == 0 { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "did not find expected version number") } *number = value return true } // Scan the value of a TAG-DIRECTIVE token. // // Scope: // %TAG !yaml! tag:yaml.org,2002: \n // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { var handle_value, prefix_value []byte // Eat whitespaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Scan a handle. if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) { return false } // Expect a whitespace. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if !is_blank(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", start_mark, "did not find expected whitespace") return false } // Eat whitespaces. for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Scan a prefix. if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) { return false } // Expect a whitespace or line break. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if !is_blankz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", start_mark, "did not find expected whitespace or line break") return false } *handle = handle_value *prefix = prefix_value return true } func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool { var s []byte // Eat the indicator character. start_mark := parser.mark skip(parser) // Consume the value. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_alpha(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } end_mark := parser.mark /* * Check if length of the anchor is greater than 0 and it is followed by * a whitespace character or one of the indicators: * * '?', ':', ',', ']', '}', '%', '@', '`'. */ if len(s) == 0 || !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') { context := "while scanning an alias" if typ == yaml_ANCHOR_TOKEN { context = "while scanning an anchor" } yaml_parser_set_scanner_error(parser, context, start_mark, "did not find expected alphabetic or numeric character") return false } // Create a token. *token = yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, value: s, } return true } /* * Scan a TAG token. */ func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool { var handle, suffix []byte start_mark := parser.mark // Check if the tag is in the canonical form. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } if parser.buffer[parser.buffer_pos+1] == '<' { // Keep the handle as '' // Eat '!<' skip(parser) skip(parser) // Consume the tag value. if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { return false } // Check for '>' and eat it. if parser.buffer[parser.buffer_pos] != '>' { yaml_parser_set_scanner_error(parser, "while scanning a tag", start_mark, "did not find the expected '>'") return false } skip(parser) } else { // The tag has either the '!suffix' or the '!handle!suffix' form. // First, try to scan a handle. if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) { return false } // Check if it is, indeed, handle. if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' { // Scan the suffix now. if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { return false } } else { // It wasn't a handle after all. Scan the rest of the tag. if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) { return false } // Set the handle to '!'. handle = []byte{'!'} // A special case: the '!' tag. Set the handle to '' and the // suffix to '!'. if len(suffix) == 0 { handle, suffix = suffix, handle } } } // Check the character which ends the tag. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if !is_blankz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a tag", start_mark, "did not find expected whitespace or line break") return false } end_mark := parser.mark // Create a token. *token = yaml_token_t{ typ: yaml_TAG_TOKEN, start_mark: start_mark, end_mark: end_mark, value: handle, suffix: suffix, } return true } // Scan a tag handle. func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool { // Check the initial '!' character. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if parser.buffer[parser.buffer_pos] != '!' { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected '!'") return false } var s []byte // Copy the '!' character. s = read(parser, s) // Copy all subsequent alphabetical and numerical characters. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_alpha(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the trailing character is '!' and copy it. if parser.buffer[parser.buffer_pos] == '!' { s = read(parser, s) } else { // It's either the '!' tag or not really a tag handle. If it's a %TAG // directive, it's an error. If it's a tag token, it must be a part of URI. if directive && !(s[0] == '!' && s[1] == 0) { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected '!'") return false } } *handle = s return true } // Scan a tag. func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { //size_t length = head ? strlen((char *)head) : 0 var s []byte length := len(head) // Copy the head if needed. // // Note that we don't copy the leading '!' character. if length > 0 { s = append(s, head[1:]...) } // Scan the tag. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } // The set of characters that may appear in URI is as follows: // // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', // '%'. // [Go] Convert this into more reasonable logic. for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' || parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' || parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' || parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' || parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' || parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' || parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '%' { // Check if it is a URI-escape sequence. if parser.buffer[parser.buffer_pos] == '%' { if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) { return false } } else { s = read(parser, s) length++ } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the tag is non-empty. if length == 0 { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected tag URI") return false } *uri = s return true } // Decode an URI-escape sequence corresponding to a single UTF-8 character. func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool { // Decode the required number of characters. w := 1024 for w > 0 { // Check for a URI-escaped octet. if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { return false } if !(parser.buffer[parser.buffer_pos] == '%' && is_hex(parser.buffer, parser.buffer_pos+1) && is_hex(parser.buffer, parser.buffer_pos+2)) { return yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find URI escaped octet") } // Get the octet. octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2)) // If it is the leading octet, determine the length of the UTF-8 sequence. if w == 1024 { w = width(octet) if w == 0 { return yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "found an incorrect leading UTF-8 octet") } } else { // Check if the trailing octet is correct. if octet&0xC0 != 0x80 { return yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "found an incorrect trailing UTF-8 octet") } } // Copy the octet and move the pointers. *s = append(*s, octet) skip(parser) skip(parser) skip(parser) w-- } return true } // Scan a block scalar. func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool { // Eat the indicator '|' or '>'. start_mark := parser.mark skip(parser) // Scan the additional block scalar indicators. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } // Check for a chomping indicator. var chomping, increment int if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { // Set the chomping method and eat the indicator. if parser.buffer[parser.buffer_pos] == '+' { chomping = +1 } else { chomping = -1 } skip(parser) // Check for an indentation indicator. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if is_digit(parser.buffer, parser.buffer_pos) { // Check that the indentation is greater than 0. if parser.buffer[parser.buffer_pos] == '0' { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found an indentation indicator equal to 0") return false } // Get the indentation level and eat the indicator. increment = as_digit(parser.buffer, parser.buffer_pos) skip(parser) } } else if is_digit(parser.buffer, parser.buffer_pos) { // Do the same as above, but in the opposite order. if parser.buffer[parser.buffer_pos] == '0' { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found an indentation indicator equal to 0") return false } increment = as_digit(parser.buffer, parser.buffer_pos) skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { if parser.buffer[parser.buffer_pos] == '+' { chomping = +1 } else { chomping = -1 } skip(parser) } } // Eat whitespaces and comments to the end of the line. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } if parser.buffer[parser.buffer_pos] == '#' { for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } } // Check if we are at the end of the line. if !is_breakz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "did not find expected comment or line break") return false } // Eat a line break. if is_break(parser.buffer, parser.buffer_pos) { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) } end_mark := parser.mark // Set the indentation level if it was specified. var indent int if increment > 0 { if parser.indent >= 0 { indent = parser.indent + increment } else { indent = increment } } // Scan the leading line breaks and determine the indentation level if needed. var s, leading_break, trailing_breaks []byte if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { return false } // Scan the block scalar content. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } var leading_blank, trailing_blank bool for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { // We are at the beginning of a non-empty line. // Is it a trailing whitespace? trailing_blank = is_blank(parser.buffer, parser.buffer_pos) // Check if we need to fold the leading line break. if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' { // Do we need to join the lines by space? if len(trailing_breaks) == 0 { s = append(s, ' ') } } else { s = append(s, leading_break...) } leading_break = leading_break[:0] // Append the remaining line breaks. s = append(s, trailing_breaks...) trailing_breaks = trailing_breaks[:0] // Is it a leading whitespace? leading_blank = is_blank(parser.buffer, parser.buffer_pos) // Consume the current line. for !is_breakz(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Consume the line break. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } leading_break = read_line(parser, leading_break) // Eat the following indentation spaces and line breaks. if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { return false } } // Chomp the tail. if chomping != -1 { s = append(s, leading_break...) } if chomping == 1 { s = append(s, trailing_breaks...) } // Create a token. *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, end_mark: end_mark, value: s, style: yaml_LITERAL_SCALAR_STYLE, } if !literal { token.style = yaml_FOLDED_SCALAR_STYLE } return true } // Scan indentation spaces and line breaks for a block scalar. Determine the // indentation level if needed. func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { *end_mark = parser.mark // Eat the indentation spaces and line breaks. max_indent := 0 for { // Eat the indentation spaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } if parser.mark.column > max_indent { max_indent = parser.mark.column } // Check for a tab character messing the indentation. if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found a tab character where an indentation space is expected") } // Have we found a non-empty line? if !is_break(parser.buffer, parser.buffer_pos) { break } // Consume the line break. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } // [Go] Should really be returning breaks instead. *breaks = read_line(parser, *breaks) *end_mark = parser.mark } // Determine the indentation level if needed. if *indent == 0 { *indent = max_indent if *indent < parser.indent+1 { *indent = parser.indent + 1 } if *indent < 1 { *indent = 1 } } return true } // Scan a quoted scalar. func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool { // Eat the left quote. start_mark := parser.mark skip(parser) // Consume the content of the quoted scalar. var s, leading_break, trailing_breaks, whitespaces []byte for { // Check that there are no document indicators at the beginning of the line. if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { return false } if parser.mark.column == 0 && ((parser.buffer[parser.buffer_pos+0] == '-' && parser.buffer[parser.buffer_pos+1] == '-' && parser.buffer[parser.buffer_pos+2] == '-') || (parser.buffer[parser.buffer_pos+0] == '.' && parser.buffer[parser.buffer_pos+1] == '.' && parser.buffer[parser.buffer_pos+2] == '.')) && is_blankz(parser.buffer, parser.buffer_pos+3) { yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", start_mark, "found unexpected document indicator") return false } // Check for EOF. if is_z(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", start_mark, "found unexpected end of stream") return false } // Consume non-blank characters. leading_blanks := false for !is_blankz(parser.buffer, parser.buffer_pos) { if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' { // Is is an escaped single quote. s = append(s, '\'') skip(parser) skip(parser) } else if single && parser.buffer[parser.buffer_pos] == '\'' { // It is a right single quote. break } else if !single && parser.buffer[parser.buffer_pos] == '"' { // It is a right double quote. break } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) { // It is an escaped line break. if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { return false } skip(parser) skip_line(parser) leading_blanks = true break } else if !single && parser.buffer[parser.buffer_pos] == '\\' { // It is an escape sequence. code_length := 0 // Check the escape character. switch parser.buffer[parser.buffer_pos+1] { case '0': s = append(s, 0) case 'a': s = append(s, '\x07') case 'b': s = append(s, '\x08') case 't', '\t': s = append(s, '\x09') case 'n': s = append(s, '\x0A') case 'v': s = append(s, '\x0B') case 'f': s = append(s, '\x0C') case 'r': s = append(s, '\x0D') case 'e': s = append(s, '\x1B') case ' ': s = append(s, '\x20') case '"': s = append(s, '"') case '\'': s = append(s, '\'') case '\\': s = append(s, '\\') case 'N': // NEL (#x85) s = append(s, '\xC2') s = append(s, '\x85') case '_': // #xA0 s = append(s, '\xC2') s = append(s, '\xA0') case 'L': // LS (#x2028) s = append(s, '\xE2') s = append(s, '\x80') s = append(s, '\xA8') case 'P': // PS (#x2029) s = append(s, '\xE2') s = append(s, '\x80') s = append(s, '\xA9') case 'x': code_length = 2 case 'u': code_length = 4 case 'U': code_length = 8 default: yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "found unknown escape character") return false } skip(parser) skip(parser) // Consume an arbitrary escape code. if code_length > 0 { var value int // Scan the character value. if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) { return false } for k := 0; k < code_length; k++ { if !is_hex(parser.buffer, parser.buffer_pos+k) { yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "did not find expected hexdecimal number") return false } value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k) } // Check the value and write the character. if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF { yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "found invalid Unicode character escape code") return false } if value <= 0x7F { s = append(s, byte(value)) } else if value <= 0x7FF { s = append(s, byte(0xC0+(value>>6))) s = append(s, byte(0x80+(value&0x3F))) } else if value <= 0xFFFF { s = append(s, byte(0xE0+(value>>12))) s = append(s, byte(0x80+((value>>6)&0x3F))) s = append(s, byte(0x80+(value&0x3F))) } else { s = append(s, byte(0xF0+(value>>18))) s = append(s, byte(0x80+((value>>12)&0x3F))) s = append(s, byte(0x80+((value>>6)&0x3F))) s = append(s, byte(0x80+(value&0x3F))) } // Advance the pointer. for k := 0; k < code_length; k++ { skip(parser) } } } else { // It is a non-escaped non-blank character. s = read(parser, s) } if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } } // Check if we are at the end of the scalar. if single { if parser.buffer[parser.buffer_pos] == '\'' { break } } else { if parser.buffer[parser.buffer_pos] == '"' { break } } // Consume blank characters. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { if is_blank(parser.buffer, parser.buffer_pos) { // Consume a space or a tab character. if !leading_blanks { whitespaces = read(parser, whitespaces) } else { skip(parser) } } else { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } // Check if it is a first line break. if !leading_blanks { whitespaces = whitespaces[:0] leading_break = read_line(parser, leading_break) leading_blanks = true } else { trailing_breaks = read_line(parser, trailing_breaks) } } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Join the whitespaces or fold line breaks. if leading_blanks { // Do we need to fold line breaks? if len(leading_break) > 0 && leading_break[0] == '\n' { if len(trailing_breaks) == 0 { s = append(s, ' ') } else { s = append(s, trailing_breaks...) } } else { s = append(s, leading_break...) s = append(s, trailing_breaks...) } trailing_breaks = trailing_breaks[:0] leading_break = leading_break[:0] } else { s = append(s, whitespaces...) whitespaces = whitespaces[:0] } } // Eat the right quote. skip(parser) end_mark := parser.mark // Create a token. *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, end_mark: end_mark, value: s, style: yaml_SINGLE_QUOTED_SCALAR_STYLE, } if !single { token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } return true } // Scan a plain scalar. func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool { var s, leading_break, trailing_breaks, whitespaces []byte var leading_blanks bool var indent = parser.indent + 1 start_mark := parser.mark end_mark := parser.mark // Consume the content of the plain scalar. for { // Check for a document indicator. if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { return false } if parser.mark.column == 0 && ((parser.buffer[parser.buffer_pos+0] == '-' && parser.buffer[parser.buffer_pos+1] == '-' && parser.buffer[parser.buffer_pos+2] == '-') || (parser.buffer[parser.buffer_pos+0] == '.' && parser.buffer[parser.buffer_pos+1] == '.' && parser.buffer[parser.buffer_pos+2] == '.')) && is_blankz(parser.buffer, parser.buffer_pos+3) { break } // Check for a comment. if parser.buffer[parser.buffer_pos] == '#' { break } // Consume non-blank characters. for !is_blankz(parser.buffer, parser.buffer_pos) { // Check for 'x:x' in the flow context. TODO: Fix the test "spec-08-13". if parser.flow_level > 0 && parser.buffer[parser.buffer_pos] == ':' && !is_blankz(parser.buffer, parser.buffer_pos+1) { yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", start_mark, "found unexpected ':'") return false } // Check for indicators that may end a plain scalar. if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) || (parser.flow_level > 0 && (parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || parser.buffer[parser.buffer_pos] == '}')) { break } // Check if we need to join whitespaces and breaks. if leading_blanks || len(whitespaces) > 0 { if leading_blanks { // Do we need to fold line breaks? if leading_break[0] == '\n' { if len(trailing_breaks) == 0 { s = append(s, ' ') } else { s = append(s, trailing_breaks...) } } else { s = append(s, leading_break...) s = append(s, trailing_breaks...) } trailing_breaks = trailing_breaks[:0] leading_break = leading_break[:0] leading_blanks = false } else { s = append(s, whitespaces...) whitespaces = whitespaces[:0] } } // Copy the character. s = read(parser, s) end_mark = parser.mark if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } } // Is it the end? if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) { break } // Consume blank characters. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { if is_blank(parser.buffer, parser.buffer_pos) { // Check for tab character that abuse indentation. if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", start_mark, "found a tab character that violate indentation") return false } // Consume a space or a tab character. if !leading_blanks { whitespaces = read(parser, whitespaces) } else { skip(parser) } } else { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } // Check if it is a first line break. if !leading_blanks { whitespaces = whitespaces[:0] leading_break = read_line(parser, leading_break) leading_blanks = true } else { trailing_breaks = read_line(parser, trailing_breaks) } } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check indentation level. if parser.flow_level == 0 && parser.mark.column < indent { break } } // Create a token. *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, end_mark: end_mark, value: s, style: yaml_PLAIN_SCALAR_STYLE, } // Note that we change the 'simple_key_allowed' flag. if leading_blanks { parser.simple_key_allowed = true } return true } ================================================ FILE: vendor/gopkg.in/yaml.v2/sorter.go ================================================ package yaml import ( "reflect" "unicode" ) type keyList []reflect.Value func (l keyList) Len() int { return len(l) } func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l keyList) Less(i, j int) bool { a := l[i] b := l[j] ak := a.Kind() bk := b.Kind() for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { a = a.Elem() ak = a.Kind() } for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { b = b.Elem() bk = b.Kind() } af, aok := keyFloat(a) bf, bok := keyFloat(b) if aok && bok { if af != bf { return af < bf } if ak != bk { return ak < bk } return numLess(a, b) } if ak != reflect.String || bk != reflect.String { return ak < bk } ar, br := []rune(a.String()), []rune(b.String()) for i := 0; i < len(ar) && i < len(br); i++ { if ar[i] == br[i] { continue } al := unicode.IsLetter(ar[i]) bl := unicode.IsLetter(br[i]) if al && bl { return ar[i] < br[i] } if al || bl { return bl } var ai, bi int var an, bn int64 for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { an = an*10 + int64(ar[ai]-'0') } for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { bn = bn*10 + int64(br[bi]-'0') } if an != bn { return an < bn } if ai != bi { return ai < bi } return ar[i] < br[i] } return len(ar) < len(br) } // keyFloat returns a float value for v if it is a number/bool // and whether it is a number/bool or not. func keyFloat(v reflect.Value) (f float64, ok bool) { switch v.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return float64(v.Int()), true case reflect.Float32, reflect.Float64: return v.Float(), true case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return float64(v.Uint()), true case reflect.Bool: if v.Bool() { return 1, true } return 0, true } return 0, false } // numLess returns whether a < b. // a and b must necessarily have the same kind. func numLess(a, b reflect.Value) bool { switch a.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return a.Int() < b.Int() case reflect.Float32, reflect.Float64: return a.Float() < b.Float() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return a.Uint() < b.Uint() case reflect.Bool: return !a.Bool() && b.Bool() } panic("not a number") } ================================================ FILE: vendor/gopkg.in/yaml.v2/suite_test.go ================================================ package yaml_test import ( . "gopkg.in/check.v1" "testing" ) func Test(t *testing.T) { TestingT(t) } type S struct{} var _ = Suite(&S{}) ================================================ FILE: vendor/gopkg.in/yaml.v2/writerc.go ================================================ package yaml // Set the writer error and return false. func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { emitter.error = yaml_WRITER_ERROR emitter.problem = problem return false } // Flush the output buffer. func yaml_emitter_flush(emitter *yaml_emitter_t) bool { if emitter.write_handler == nil { panic("write handler not set") } // Check if the buffer is empty. if emitter.buffer_pos == 0 { return true } // If the output encoding is UTF-8, we don't need to recode the buffer. if emitter.encoding == yaml_UTF8_ENCODING { if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) } emitter.buffer_pos = 0 return true } // Recode the buffer into the raw buffer. var low, high int if emitter.encoding == yaml_UTF16LE_ENCODING { low, high = 0, 1 } else { high, low = 1, 0 } pos := 0 for pos < emitter.buffer_pos { // See the "reader.c" code for more details on UTF-8 encoding. Note // that we assume that the buffer contains a valid UTF-8 sequence. // Read the next UTF-8 character. octet := emitter.buffer[pos] var w int var value rune switch { case octet&0x80 == 0x00: w, value = 1, rune(octet&0x7F) case octet&0xE0 == 0xC0: w, value = 2, rune(octet&0x1F) case octet&0xF0 == 0xE0: w, value = 3, rune(octet&0x0F) case octet&0xF8 == 0xF0: w, value = 4, rune(octet&0x07) } for k := 1; k < w; k++ { octet = emitter.buffer[pos+k] value = (value << 6) + (rune(octet) & 0x3F) } pos += w // Write the character. if value < 0x10000 { var b [2]byte b[high] = byte(value >> 8) b[low] = byte(value & 0xFF) emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1]) } else { // Write the character using a surrogate pair (check "reader.c"). var b [4]byte value -= 0x10000 b[high] = byte(0xD8 + (value >> 18)) b[low] = byte((value >> 10) & 0xFF) b[high+2] = byte(0xDC + ((value >> 8) & 0xFF)) b[low+2] = byte(value & 0xFF) emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3]) } } // Write the raw buffer. if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil { return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) } emitter.buffer_pos = 0 emitter.raw_buffer = emitter.raw_buffer[:0] return true } ================================================ FILE: vendor/gopkg.in/yaml.v2/yaml.go ================================================ // Package yaml implements YAML support for the Go language. // // Source code and other details for the project are available at GitHub: // // https://github.com/go-yaml/yaml // package yaml import ( "errors" "fmt" "reflect" "strings" "sync" ) // MapSlice encodes and decodes as a YAML map. // The order of keys is preserved when encoding and decoding. type MapSlice []MapItem // MapItem is an item in a MapSlice. type MapItem struct { Key, Value interface{} } // The Unmarshaler interface may be implemented by types to customize their // behavior when being unmarshaled from a YAML document. The UnmarshalYAML // method receives a function that may be called to unmarshal the original // YAML value into a field or variable. It is safe to call the unmarshal // function parameter more than once if necessary. type Unmarshaler interface { UnmarshalYAML(unmarshal func(interface{}) error) error } // The Marshaler interface may be implemented by types to customize their // behavior when being marshaled into a YAML document. The returned value // is marshaled in place of the original value implementing Marshaler. // // If an error is returned by MarshalYAML, the marshaling procedure stops // and returns with the provided error. type Marshaler interface { MarshalYAML() (interface{}, error) } // Unmarshal decodes the first document found within the in byte slice // and assigns decoded values into the out value. // // Maps and pointers (to a struct, string, int, etc) are accepted as out // values. If an internal pointer within a struct is not initialized, // the yaml package will initialize it if necessary for unmarshalling // the provided data. The out parameter must not be nil. // // The type of the decoded values should be compatible with the respective // values in out. If one or more values cannot be decoded due to a type // mismatches, decoding continues partially until the end of the YAML // content, and a *yaml.TypeError is returned with details for all // missed values. // // Struct fields are only unmarshalled if they are exported (have an // upper case first letter), and are unmarshalled using the field name // lowercased as the default key. Custom keys may be defined via the // "yaml" name in the field tag: the content preceding the first comma // is used as the key, and the following comma-separated options are // used to tweak the marshalling process (see Marshal). // Conflicting names result in a runtime error. // // For example: // // type T struct { // F int `yaml:"a,omitempty"` // B int // } // var t T // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) // // See the documentation of Marshal for the format of tags and a list of // supported tag options. // func Unmarshal(in []byte, out interface{}) (err error) { return unmarshal(in, out, false) } // UnmarshalStrict is like Unmarshal except that any fields that are found // in the data that do not have corresponding struct members will result in // an error. func UnmarshalStrict(in []byte, out interface{}) (err error) { return unmarshal(in, out, true) } func unmarshal(in []byte, out interface{}, strict bool) (err error) { defer handleErr(&err) d := newDecoder(strict) p := newParser(in) defer p.destroy() node := p.parse() if node != nil { v := reflect.ValueOf(out) if v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() } d.unmarshal(node, v) } if len(d.terrors) > 0 { return &TypeError{d.terrors} } return nil } // Marshal serializes the value provided into a YAML document. The structure // of the generated document will reflect the structure of the value itself. // Maps and pointers (to struct, string, int, etc) are accepted as the in value. // // Struct fields are only unmarshalled if they are exported (have an upper case // first letter), and are unmarshalled using the field name lowercased as the // default key. Custom keys may be defined via the "yaml" name in the field // tag: the content preceding the first comma is used as the key, and the // following comma-separated options are used to tweak the marshalling process. // Conflicting names result in a runtime error. // // The field tag format accepted is: // // `(...) yaml:"[][,[,]]" (...)` // // The following flags are currently supported: // // omitempty Only include the field if it's not set to the zero // value for the type or to empty slices or maps. // Does not apply to zero valued structs. // // flow Marshal using a flow style (useful for structs, // sequences and maps). // // inline Inline the field, which must be a struct or a map, // causing all of its fields or keys to be processed as if // they were part of the outer struct. For maps, keys must // not conflict with the yaml keys of other struct fields. // // In addition, if the key is "-", the field is ignored. // // For example: // // type T struct { // F int "a,omitempty" // B int // } // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" // yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" // func Marshal(in interface{}) (out []byte, err error) { defer handleErr(&err) e := newEncoder() defer e.destroy() e.marshal("", reflect.ValueOf(in)) e.finish() out = e.out return } func handleErr(err *error) { if v := recover(); v != nil { if e, ok := v.(yamlError); ok { *err = e.err } else { panic(v) } } } type yamlError struct { err error } func fail(err error) { panic(yamlError{err}) } func failf(format string, args ...interface{}) { panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) } // A TypeError is returned by Unmarshal when one or more fields in // the YAML document cannot be properly decoded into the requested // types. When this error is returned, the value is still // unmarshaled partially. type TypeError struct { Errors []string } func (e *TypeError) Error() string { return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) } // -------------------------------------------------------------------------- // Maintain a mapping of keys to structure field indexes // The code in this section was copied from mgo/bson. // structInfo holds details for the serialization of fields of // a given struct. type structInfo struct { FieldsMap map[string]fieldInfo FieldsList []fieldInfo // InlineMap is the number of the field in the struct that // contains an ,inline map, or -1 if there's none. InlineMap int } type fieldInfo struct { Key string Num int OmitEmpty bool Flow bool // Inline holds the field index if the field is part of an inlined struct. Inline []int } var structMap = make(map[reflect.Type]*structInfo) var fieldMapMutex sync.RWMutex func getStructInfo(st reflect.Type) (*structInfo, error) { fieldMapMutex.RLock() sinfo, found := structMap[st] fieldMapMutex.RUnlock() if found { return sinfo, nil } n := st.NumField() fieldsMap := make(map[string]fieldInfo) fieldsList := make([]fieldInfo, 0, n) inlineMap := -1 for i := 0; i != n; i++ { field := st.Field(i) if field.PkgPath != "" && !field.Anonymous { continue // Private field } info := fieldInfo{Num: i} tag := field.Tag.Get("yaml") if tag == "" && strings.Index(string(field.Tag), ":") < 0 { tag = string(field.Tag) } if tag == "-" { continue } inline := false fields := strings.Split(tag, ",") if len(fields) > 1 { for _, flag := range fields[1:] { switch flag { case "omitempty": info.OmitEmpty = true case "flow": info.Flow = true case "inline": inline = true default: return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)) } } tag = fields[0] } if inline { switch field.Type.Kind() { case reflect.Map: if inlineMap >= 0 { return nil, errors.New("Multiple ,inline maps in struct " + st.String()) } if field.Type.Key() != reflect.TypeOf("") { return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) } inlineMap = info.Num case reflect.Struct: sinfo, err := getStructInfo(field.Type) if err != nil { return nil, err } for _, finfo := range sinfo.FieldsList { if _, found := fieldsMap[finfo.Key]; found { msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() return nil, errors.New(msg) } if finfo.Inline == nil { finfo.Inline = []int{i, finfo.Num} } else { finfo.Inline = append([]int{i}, finfo.Inline...) } fieldsMap[finfo.Key] = finfo fieldsList = append(fieldsList, finfo) } default: //return nil, errors.New("Option ,inline needs a struct value or map field") return nil, errors.New("Option ,inline needs a struct value field") } continue } if tag != "" { info.Key = tag } else { info.Key = strings.ToLower(field.Name) } if _, found = fieldsMap[info.Key]; found { msg := "Duplicated key '" + info.Key + "' in struct " + st.String() return nil, errors.New(msg) } fieldsList = append(fieldsList, info) fieldsMap[info.Key] = info } sinfo = &structInfo{fieldsMap, fieldsList, inlineMap} fieldMapMutex.Lock() structMap[st] = sinfo fieldMapMutex.Unlock() return sinfo, nil } func isZero(v reflect.Value) bool { switch v.Kind() { case reflect.String: return len(v.String()) == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() case reflect.Slice: return v.Len() == 0 case reflect.Map: return v.Len() == 0 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Bool: return !v.Bool() case reflect.Struct: vt := v.Type() for i := v.NumField() - 1; i >= 0; i-- { if vt.Field(i).PkgPath != "" { continue // Private field } if !isZero(v.Field(i)) { return false } } return true } return false } ================================================ FILE: vendor/gopkg.in/yaml.v2/yamlh.go ================================================ package yaml import ( "io" ) // The version directive data. type yaml_version_directive_t struct { major int8 // The major version number. minor int8 // The minor version number. } // The tag directive data. type yaml_tag_directive_t struct { handle []byte // The tag handle. prefix []byte // The tag prefix. } type yaml_encoding_t int // The stream encoding. const ( // Let the parser choose the encoding. yaml_ANY_ENCODING yaml_encoding_t = iota yaml_UTF8_ENCODING // The default UTF-8 encoding. yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. ) type yaml_break_t int // Line break types. const ( // Let the parser choose the break type. yaml_ANY_BREAK yaml_break_t = iota yaml_CR_BREAK // Use CR for line breaks (Mac style). yaml_LN_BREAK // Use LN for line breaks (Unix style). yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). ) type yaml_error_type_t int // Many bad things could happen with the parser and emitter. const ( // No error is produced. yaml_NO_ERROR yaml_error_type_t = iota yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. yaml_READER_ERROR // Cannot read or decode the input stream. yaml_SCANNER_ERROR // Cannot scan the input stream. yaml_PARSER_ERROR // Cannot parse the input stream. yaml_COMPOSER_ERROR // Cannot compose a YAML document. yaml_WRITER_ERROR // Cannot write to the output stream. yaml_EMITTER_ERROR // Cannot emit a YAML stream. ) // The pointer position. type yaml_mark_t struct { index int // The position index. line int // The position line. column int // The position column. } // Node Styles type yaml_style_t int8 type yaml_scalar_style_t yaml_style_t // Scalar styles. const ( // Let the emitter choose the style. yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota yaml_PLAIN_SCALAR_STYLE // The plain scalar style. yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. yaml_LITERAL_SCALAR_STYLE // The literal scalar style. yaml_FOLDED_SCALAR_STYLE // The folded scalar style. ) type yaml_sequence_style_t yaml_style_t // Sequence styles. const ( // Let the emitter choose the style. yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. ) type yaml_mapping_style_t yaml_style_t // Mapping styles. const ( // Let the emitter choose the style. yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota yaml_BLOCK_MAPPING_STYLE // The block mapping style. yaml_FLOW_MAPPING_STYLE // The flow mapping style. ) // Tokens type yaml_token_type_t int // Token types. const ( // An empty token. yaml_NO_TOKEN yaml_token_type_t = iota yaml_STREAM_START_TOKEN // A STREAM-START token. yaml_STREAM_END_TOKEN // A STREAM-END token. yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. yaml_BLOCK_END_TOKEN // A BLOCK-END token. yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. yaml_KEY_TOKEN // A KEY token. yaml_VALUE_TOKEN // A VALUE token. yaml_ALIAS_TOKEN // An ALIAS token. yaml_ANCHOR_TOKEN // An ANCHOR token. yaml_TAG_TOKEN // A TAG token. yaml_SCALAR_TOKEN // A SCALAR token. ) func (tt yaml_token_type_t) String() string { switch tt { case yaml_NO_TOKEN: return "yaml_NO_TOKEN" case yaml_STREAM_START_TOKEN: return "yaml_STREAM_START_TOKEN" case yaml_STREAM_END_TOKEN: return "yaml_STREAM_END_TOKEN" case yaml_VERSION_DIRECTIVE_TOKEN: return "yaml_VERSION_DIRECTIVE_TOKEN" case yaml_TAG_DIRECTIVE_TOKEN: return "yaml_TAG_DIRECTIVE_TOKEN" case yaml_DOCUMENT_START_TOKEN: return "yaml_DOCUMENT_START_TOKEN" case yaml_DOCUMENT_END_TOKEN: return "yaml_DOCUMENT_END_TOKEN" case yaml_BLOCK_SEQUENCE_START_TOKEN: return "yaml_BLOCK_SEQUENCE_START_TOKEN" case yaml_BLOCK_MAPPING_START_TOKEN: return "yaml_BLOCK_MAPPING_START_TOKEN" case yaml_BLOCK_END_TOKEN: return "yaml_BLOCK_END_TOKEN" case yaml_FLOW_SEQUENCE_START_TOKEN: return "yaml_FLOW_SEQUENCE_START_TOKEN" case yaml_FLOW_SEQUENCE_END_TOKEN: return "yaml_FLOW_SEQUENCE_END_TOKEN" case yaml_FLOW_MAPPING_START_TOKEN: return "yaml_FLOW_MAPPING_START_TOKEN" case yaml_FLOW_MAPPING_END_TOKEN: return "yaml_FLOW_MAPPING_END_TOKEN" case yaml_BLOCK_ENTRY_TOKEN: return "yaml_BLOCK_ENTRY_TOKEN" case yaml_FLOW_ENTRY_TOKEN: return "yaml_FLOW_ENTRY_TOKEN" case yaml_KEY_TOKEN: return "yaml_KEY_TOKEN" case yaml_VALUE_TOKEN: return "yaml_VALUE_TOKEN" case yaml_ALIAS_TOKEN: return "yaml_ALIAS_TOKEN" case yaml_ANCHOR_TOKEN: return "yaml_ANCHOR_TOKEN" case yaml_TAG_TOKEN: return "yaml_TAG_TOKEN" case yaml_SCALAR_TOKEN: return "yaml_SCALAR_TOKEN" } return "" } // The token structure. type yaml_token_t struct { // The token type. typ yaml_token_type_t // The start/end of the token. start_mark, end_mark yaml_mark_t // The stream encoding (for yaml_STREAM_START_TOKEN). encoding yaml_encoding_t // The alias/anchor/scalar value or tag/tag directive handle // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). value []byte // The tag suffix (for yaml_TAG_TOKEN). suffix []byte // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). prefix []byte // The scalar style (for yaml_SCALAR_TOKEN). style yaml_scalar_style_t // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). major, minor int8 } // Events type yaml_event_type_t int8 // Event types. const ( // An empty event. yaml_NO_EVENT yaml_event_type_t = iota yaml_STREAM_START_EVENT // A STREAM-START event. yaml_STREAM_END_EVENT // A STREAM-END event. yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. yaml_ALIAS_EVENT // An ALIAS event. yaml_SCALAR_EVENT // A SCALAR event. yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. yaml_MAPPING_START_EVENT // A MAPPING-START event. yaml_MAPPING_END_EVENT // A MAPPING-END event. ) // The event structure. type yaml_event_t struct { // The event type. typ yaml_event_type_t // The start and end of the event. start_mark, end_mark yaml_mark_t // The document encoding (for yaml_STREAM_START_EVENT). encoding yaml_encoding_t // The version directive (for yaml_DOCUMENT_START_EVENT). version_directive *yaml_version_directive_t // The list of tag directives (for yaml_DOCUMENT_START_EVENT). tag_directives []yaml_tag_directive_t // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). anchor []byte // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). tag []byte // The scalar value (for yaml_SCALAR_EVENT). value []byte // Is the document start/end indicator implicit, or the tag optional? // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). implicit bool // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). quoted_implicit bool // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). style yaml_style_t } func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } // Nodes const ( yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. // Not in original libyaml. yaml_BINARY_TAG = "tag:yaml.org,2002:binary" yaml_MERGE_TAG = "tag:yaml.org,2002:merge" yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. ) type yaml_node_type_t int // Node types. const ( // An empty node. yaml_NO_NODE yaml_node_type_t = iota yaml_SCALAR_NODE // A scalar node. yaml_SEQUENCE_NODE // A sequence node. yaml_MAPPING_NODE // A mapping node. ) // An element of a sequence node. type yaml_node_item_t int // An element of a mapping node. type yaml_node_pair_t struct { key int // The key of the element. value int // The value of the element. } // The node structure. type yaml_node_t struct { typ yaml_node_type_t // The node type. tag []byte // The node tag. // The node data. // The scalar parameters (for yaml_SCALAR_NODE). scalar struct { value []byte // The scalar value. length int // The length of the scalar value. style yaml_scalar_style_t // The scalar style. } // The sequence parameters (for YAML_SEQUENCE_NODE). sequence struct { items_data []yaml_node_item_t // The stack of sequence items. style yaml_sequence_style_t // The sequence style. } // The mapping parameters (for yaml_MAPPING_NODE). mapping struct { pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). pairs_start *yaml_node_pair_t // The beginning of the stack. pairs_end *yaml_node_pair_t // The end of the stack. pairs_top *yaml_node_pair_t // The top of the stack. style yaml_mapping_style_t // The mapping style. } start_mark yaml_mark_t // The beginning of the node. end_mark yaml_mark_t // The end of the node. } // The document structure. type yaml_document_t struct { // The document nodes. nodes []yaml_node_t // The version directive. version_directive *yaml_version_directive_t // The list of tag directives. tag_directives_data []yaml_tag_directive_t tag_directives_start int // The beginning of the tag directives list. tag_directives_end int // The end of the tag directives list. start_implicit int // Is the document start indicator implicit? end_implicit int // Is the document end indicator implicit? // The start/end of the document. start_mark, end_mark yaml_mark_t } // The prototype of a read handler. // // The read handler is called when the parser needs to read more bytes from the // source. The handler should write not more than size bytes to the buffer. // The number of written bytes should be set to the size_read variable. // // [in,out] data A pointer to an application data specified by // yaml_parser_set_input(). // [out] buffer The buffer to write the data from the source. // [in] size The size of the buffer. // [out] size_read The actual number of bytes read from the source. // // On success, the handler should return 1. If the handler failed, // the returned value should be 0. On EOF, the handler should set the // size_read to 0 and return 1. type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) // This structure holds information about a potential simple key. type yaml_simple_key_t struct { possible bool // Is a simple key possible? required bool // Is a simple key required? token_number int // The number of the token. mark yaml_mark_t // The position mark. } // The states of the parser. type yaml_parser_state_t int const ( yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. yaml_PARSE_END_STATE // Expect nothing. ) func (ps yaml_parser_state_t) String() string { switch ps { case yaml_PARSE_STREAM_START_STATE: return "yaml_PARSE_STREAM_START_STATE" case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" case yaml_PARSE_DOCUMENT_START_STATE: return "yaml_PARSE_DOCUMENT_START_STATE" case yaml_PARSE_DOCUMENT_CONTENT_STATE: return "yaml_PARSE_DOCUMENT_CONTENT_STATE" case yaml_PARSE_DOCUMENT_END_STATE: return "yaml_PARSE_DOCUMENT_END_STATE" case yaml_PARSE_BLOCK_NODE_STATE: return "yaml_PARSE_BLOCK_NODE_STATE" case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" case yaml_PARSE_FLOW_NODE_STATE: return "yaml_PARSE_FLOW_NODE_STATE" case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" case yaml_PARSE_FLOW_MAPPING_KEY_STATE: return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" case yaml_PARSE_END_STATE: return "yaml_PARSE_END_STATE" } return "" } // This structure holds aliases data. type yaml_alias_data_t struct { anchor []byte // The anchor. index int // The node id. mark yaml_mark_t // The anchor mark. } // The parser structure. // // All members are internal. Manage the structure using the // yaml_parser_ family of functions. type yaml_parser_t struct { // Error handling error yaml_error_type_t // Error type. problem string // Error description. // The byte about which the problem occurred. problem_offset int problem_value int problem_mark yaml_mark_t // The error context. context string context_mark yaml_mark_t // Reader stuff read_handler yaml_read_handler_t // Read handler. input_file io.Reader // File input data. input []byte // String input data. input_pos int eof bool // EOF flag buffer []byte // The working buffer. buffer_pos int // The current position of the buffer. unread int // The number of unread characters in the buffer. raw_buffer []byte // The raw buffer. raw_buffer_pos int // The current position of the buffer. encoding yaml_encoding_t // The input encoding. offset int // The offset of the current position (in bytes). mark yaml_mark_t // The mark of the current position. // Scanner stuff stream_start_produced bool // Have we started to scan the input stream? stream_end_produced bool // Have we reached the end of the input stream? flow_level int // The number of unclosed '[' and '{' indicators. tokens []yaml_token_t // The tokens queue. tokens_head int // The head of the tokens queue. tokens_parsed int // The number of tokens fetched from the queue. token_available bool // Does the tokens queue contain a token ready for dequeueing. indent int // The current indentation level. indents []int // The indentation levels stack. simple_key_allowed bool // May a simple key occur at the current position? simple_keys []yaml_simple_key_t // The stack of simple keys. // Parser stuff state yaml_parser_state_t // The current parser state. states []yaml_parser_state_t // The parser states stack. marks []yaml_mark_t // The stack of marks. tag_directives []yaml_tag_directive_t // The list of TAG directives. // Dumper stuff aliases []yaml_alias_data_t // The alias data. document *yaml_document_t // The currently parsed document. } // Emitter Definitions // The prototype of a write handler. // // The write handler is called when the emitter needs to flush the accumulated // characters to the output. The handler should write @a size bytes of the // @a buffer to the output. // // @param[in,out] data A pointer to an application data specified by // yaml_emitter_set_output(). // @param[in] buffer The buffer with bytes to be written. // @param[in] size The size of the buffer. // // @returns On success, the handler should return @c 1. If the handler failed, // the returned value should be @c 0. // type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error type yaml_emitter_state_t int // The emitter states. const ( // Expect STREAM-START. yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. yaml_EMIT_END_STATE // Expect nothing. ) // The emitter structure. // // All members are internal. Manage the structure using the @c yaml_emitter_ // family of functions. type yaml_emitter_t struct { // Error handling error yaml_error_type_t // Error type. problem string // Error description. // Writer stuff write_handler yaml_write_handler_t // Write handler. output_buffer *[]byte // String output data. output_file io.Writer // File output data. buffer []byte // The working buffer. buffer_pos int // The current position of the buffer. raw_buffer []byte // The raw buffer. raw_buffer_pos int // The current position of the buffer. encoding yaml_encoding_t // The stream encoding. // Emitter stuff canonical bool // If the output is in the canonical style? best_indent int // The number of indentation spaces. best_width int // The preferred width of the output lines. unicode bool // Allow unescaped non-ASCII characters? line_break yaml_break_t // The preferred line break. state yaml_emitter_state_t // The current emitter state. states []yaml_emitter_state_t // The stack of states. events []yaml_event_t // The event queue. events_head int // The head of the event queue. indents []int // The stack of indentation levels. tag_directives []yaml_tag_directive_t // The list of tag directives. indent int // The current indentation level. flow_level int // The current flow level. root_context bool // Is it the document root context? sequence_context bool // Is it a sequence context? mapping_context bool // Is it a mapping context? simple_key_context bool // Is it a simple mapping key context? line int // The current line. column int // The current column. whitespace bool // If the last character was a whitespace? indention bool // If the last character was an indentation character (' ', '-', '?', ':')? open_ended bool // If an explicit document end is required? // Anchor analysis. anchor_data struct { anchor []byte // The anchor value. alias bool // Is it an alias? } // Tag analysis. tag_data struct { handle []byte // The tag handle. suffix []byte // The tag suffix. } // Scalar analysis. scalar_data struct { value []byte // The scalar value. multiline bool // Does the scalar contain line breaks? flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? block_plain_allowed bool // Can the scalar be expressed in the block plain style? single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? block_allowed bool // Can the scalar be expressed in the literal or folded styles? style yaml_scalar_style_t // The output style. } // Dumper stuff opened bool // If the stream was already opened? closed bool // If the stream was already closed? // The information associated with the document nodes. anchors *struct { references int // The number of references. anchor int // The anchor id. serialized bool // If the node has been emitted? } last_anchor_id int // The last assigned anchor id. document *yaml_document_t // The currently emitted document. } ================================================ FILE: vendor/gopkg.in/yaml.v2/yamlprivateh.go ================================================ package yaml const ( // The size of the input raw buffer. input_raw_buffer_size = 512 // The size of the input buffer. // It should be possible to decode the whole raw buffer. input_buffer_size = input_raw_buffer_size * 3 // The size of the output buffer. output_buffer_size = 128 // The size of the output raw buffer. // It should be possible to encode the whole output buffer. output_raw_buffer_size = (output_buffer_size*2 + 2) // The size of other stacks and queues. initial_stack_size = 16 initial_queue_size = 16 initial_string_size = 16 ) // Check if the character at the specified position is an alphabetical // character, a digit, '_', or '-'. func is_alpha(b []byte, i int) bool { return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' } // Check if the character at the specified position is a digit. func is_digit(b []byte, i int) bool { return b[i] >= '0' && b[i] <= '9' } // Get the value of a digit. func as_digit(b []byte, i int) int { return int(b[i]) - '0' } // Check if the character at the specified position is a hex-digit. func is_hex(b []byte, i int) bool { return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' } // Get the value of a hex-digit. func as_hex(b []byte, i int) int { bi := b[i] if bi >= 'A' && bi <= 'F' { return int(bi) - 'A' + 10 } if bi >= 'a' && bi <= 'f' { return int(bi) - 'a' + 10 } return int(bi) - '0' } // Check if the character is ASCII. func is_ascii(b []byte, i int) bool { return b[i] <= 0x7F } // Check if the character at the start of the buffer can be printed unescaped. func is_printable(b []byte, i int) bool { return ((b[i] == 0x0A) || // . == #x0A (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF (b[i] > 0xC2 && b[i] < 0xED) || (b[i] == 0xED && b[i+1] < 0xA0) || (b[i] == 0xEE) || (b[i] == 0xEF && // #xE000 <= . <= #xFFFD !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) } // Check if the character at the specified position is NUL. func is_z(b []byte, i int) bool { return b[i] == 0x00 } // Check if the beginning of the buffer is a BOM. func is_bom(b []byte, i int) bool { return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF } // Check if the character at the specified position is space. func is_space(b []byte, i int) bool { return b[i] == ' ' } // Check if the character at the specified position is tab. func is_tab(b []byte, i int) bool { return b[i] == '\t' } // Check if the character at the specified position is blank (space or tab). func is_blank(b []byte, i int) bool { //return is_space(b, i) || is_tab(b, i) return b[i] == ' ' || b[i] == '\t' } // Check if the character at the specified position is a line break. func is_break(b []byte, i int) bool { return (b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) } func is_crlf(b []byte, i int) bool { return b[i] == '\r' && b[i+1] == '\n' } // Check if the character is a line break or NUL. func is_breakz(b []byte, i int) bool { //return is_break(b, i) || is_z(b, i) return ( // is_break: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) // is_z: b[i] == 0) } // Check if the character is a line break, space, or NUL. func is_spacez(b []byte, i int) bool { //return is_space(b, i) || is_breakz(b, i) return ( // is_space: b[i] == ' ' || // is_breakz: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) b[i] == 0) } // Check if the character is a line break, space, tab, or NUL. func is_blankz(b []byte, i int) bool { //return is_blank(b, i) || is_breakz(b, i) return ( // is_blank: b[i] == ' ' || b[i] == '\t' || // is_breakz: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) b[i] == 0) } // Determine the width of the character. func width(b byte) int { // Don't replace these by a switch without first // confirming that it is being inlined. if b&0x80 == 0x00 { return 1 } if b&0xE0 == 0xC0 { return 2 } if b&0xF0 == 0xE0 { return 3 } if b&0xF8 == 0xF0 { return 4 } return 0 }