glide-0.13.1/000077500000000000000000000000001320041442700126555ustar00rootroot00000000000000glide-0.13.1/.travis.yml000066400000000000000000000013751320041442700147740ustar00rootroot00000000000000language: go go: - 1.5.x - 1.6.x - 1.7.x - 1.8.x - 1.9.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: - GO15VENDOREXPERIMENT=1 make test integration-test verify-version notifications: webhooks: urls: - https://webhooks.gitter.im/e/67e4b42cbf763625d0b4 on_success: change on_failure: always on_start: never glide-0.13.1/CHANGELOG.md000066400000000000000000000457051320041442700145010ustar00rootroot00000000000000# 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) glide-0.13.1/LICENSE000066400000000000000000000022451320041442700136650ustar00rootroot00000000000000Glide 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. glide-0.13.1/Makefile000066400000000000000000000035261320041442700143230ustar00rootroot00000000000000GLIDE_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 glide-0.13.1/README.md000066400000000000000000000475011320041442700141430ustar00rootroot00000000000000# 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) [![Join the chat at https://gitter.im/Masterminds/glide](https://badges.gitter.im/Masterminds/glide.svg)](https://gitter.im/Masterminds/glide?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ### Golang Dep The Go community now has the [dep](https://github.com/golang/dep) project to manage dependencies. Please consider trying to migrate from Glide to dep. If there is an issue preventing you from migrating please file an issue with dep so the problem can be corrected. Glide will continue to be supported for some time but is considered to be in a state of support rather than active feature development. ### 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. glide-0.13.1/action/000077500000000000000000000000001320041442700141325ustar00rootroot00000000000000glide-0.13.1/action/about.go000066400000000000000000000033311320041442700155730ustar00rootroot00000000000000package 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) } glide-0.13.1/action/about_test.go000066400000000000000000000005221320041442700166310ustar00rootroot00000000000000package 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 } glide-0.13.1/action/cache.go000066400000000000000000000005541320041442700155300ustar00rootroot00000000000000package 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.") } glide-0.13.1/action/config_wizard.go000066400000000000000000000236451320041442700173200ustar00rootroot00000000000000package 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 "" } glide-0.13.1/action/create.go000066400000000000000000000155561320041442700157400ustar00rootroot00000000000000package 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 } glide-0.13.1/action/debug.go000066400000000000000000000005431320041442700155510ustar00rootroot00000000000000package 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 } glide-0.13.1/action/doc.go000066400000000000000000000012151320041442700152250ustar00rootroot00000000000000// 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 glide-0.13.1/action/ensure.go000066400000000000000000000113731320041442700157670ustar00rootroot00000000000000package 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 } glide-0.13.1/action/get.go000066400000000000000000000156641320041442700152540ustar00rootroot00000000000000package 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() } } } } } glide-0.13.1/action/get_test.go000066400000000000000000000020771320041442700163050ustar00rootroot00000000000000package 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 } glide-0.13.1/action/import_gb.go000066400000000000000000000023351320041442700164460ustar00rootroot00000000000000package 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) } } glide-0.13.1/action/import_godep.go000066400000000000000000000006761320041442700171620ustar00rootroot00000000000000package 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) } glide-0.13.1/action/import_gom.go000066400000000000000000000006531320041442700166410ustar00rootroot00000000000000package 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) } glide-0.13.1/action/import_gpm.go000066400000000000000000000006731320041442700166440ustar00rootroot00000000000000package 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) } glide-0.13.1/action/init.go000066400000000000000000000003541320041442700154260ustar00rootroot00000000000000package 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) } glide-0.13.1/action/install.go000066400000000000000000000032471320041442700161350ustar00rootroot00000000000000package 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) } } } glide-0.13.1/action/list.go000066400000000000000000000044501320041442700154370ustar00rootroot00000000000000package 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") } } glide-0.13.1/action/list_test.go000066400000000000000000000017461320041442700165030ustar00rootroot00000000000000package 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") } } glide-0.13.1/action/mirrors.go000066400000000000000000000055641320041442700161700ustar00rootroot00000000000000package 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 } glide-0.13.1/action/name.go000066400000000000000000000003071320041442700154010ustar00rootroot00000000000000package 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) } glide-0.13.1/action/name_test.go000066400000000000000000000010071320041442700164360ustar00rootroot00000000000000package 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) } glide-0.13.1/action/no_vendor.go000066400000000000000000000056721320041442700164640ustar00rootroot00000000000000package 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" } glide-0.13.1/action/no_vendor_test.go000066400000000000000000000002711320041442700175110ustar00rootroot00000000000000package action import ( "testing" "github.com/Masterminds/glide/msg" ) func TestNoVendor(t *testing.T) { msg.Default.PanicOnDie = true NoVendor("../testdata/nv", false, false) } glide-0.13.1/action/plugin.go000066400000000000000000000024241320041442700157610ustar00rootroot00000000000000package 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) } } glide-0.13.1/action/plugin_test.go000066400000000000000000000010441320041442700170150ustar00rootroot00000000000000package 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) } glide-0.13.1/action/project_info.go000066400000000000000000000013641320041442700171460ustar00rootroot00000000000000package 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()) } glide-0.13.1/action/rebuild.go000066400000000000000000000046171320041442700161170ustar00rootroot00000000000000package 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 } glide-0.13.1/action/rebuild_test.go000066400000000000000000000004631320041442700171510ustar00rootroot00000000000000package 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) } glide-0.13.1/action/remove.go000066400000000000000000000032171320041442700157610ustar00rootroot00000000000000package 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 } glide-0.13.1/action/tree.go000066400000000000000000000013651320041442700154250ustar00rootroot00000000000000package 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) } glide-0.13.1/action/update.go000066400000000000000000000070011320041442700157410ustar00rootroot00000000000000package 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) } } } glide-0.13.1/appveyor.yml000066400000000000000000000005641320041442700152520ustar00rootroot00000000000000 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 glide-0.13.1/cache/000077500000000000000000000000001320041442700137205ustar00rootroot00000000000000glide-0.13.1/cache/cache.go000066400000000000000000000123111320041442700153100ustar00rootroot00000000000000// 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() } glide-0.13.1/cache/cache_test.go000066400000000000000000000007771320041442700163640ustar00rootroot00000000000000package 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) } } } glide-0.13.1/cache/global_lock.go000066400000000000000000000050351320041442700165220ustar00rootroot00000000000000package 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) } } glide-0.13.1/cache/memory.go000066400000000000000000000055521320041442700155660ustar00rootroot00000000000000package 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] } glide-0.13.1/cfg/000077500000000000000000000000001320041442700134145ustar00rootroot00000000000000glide-0.13.1/cfg/cfg.go000066400000000000000000000061101320041442700145000ustar00rootroot00000000000000// 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 glide-0.13.1/cfg/config.go000066400000000000000000000364161320041442700152220ustar00rootroot00000000000000package 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) } glide-0.13.1/cfg/config_test.go000066400000000000000000000075661320041442700162650ustar00rootroot00000000000000package 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") } } glide-0.13.1/cfg/lock.go000066400000000000000000000136761320041442700147100ustar00rootroot00000000000000package 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 } glide-0.13.1/cfg/lock_test.go000066400000000000000000000044451320041442700157410ustar00rootroot00000000000000package 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) } } glide-0.13.1/dependency/000077500000000000000000000000001320041442700147735ustar00rootroot00000000000000glide-0.13.1/dependency/resolver.go000066400000000000000000001075671320041442700172030ustar00rootroot00000000000000package 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{}{}, } // 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 } glide-0.13.1/dependency/resolver_test.go000066400000000000000000000046601320041442700202300ustar00rootroot00000000000000package 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)) } } glide-0.13.1/dependency/scan.go000066400000000000000000000152641320041442700162560ustar00rootroot00000000000000package 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 } glide-0.13.1/docs/000077500000000000000000000000001320041442700136055ustar00rootroot00000000000000glide-0.13.1/docs/commands.md000066400000000000000000000146421320041442700157370ustar00rootroot00000000000000# 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 glide-0.13.1/docs/example-glide.yaml000066400000000000000000000046161320041442700172150ustar00rootroot00000000000000# 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 glide-0.13.1/docs/faq.md000066400000000000000000000054301320041442700147000ustar00rootroot00000000000000# 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. glide-0.13.1/docs/getting-started.md000066400000000000000000000106371320041442700172430ustar00rootroot00000000000000# 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. glide-0.13.1/docs/glide-plugin-example000077500000000000000000000012461320041442700175470ustar00rootroot00000000000000#!/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" glide-0.13.1/docs/glide.lock.md000066400000000000000000000017561320041442700161530ustar00rootroot00000000000000# 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. glide-0.13.1/docs/glide.yaml.md000066400000000000000000000075301320041442700161610ustar00rootroot00000000000000# 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. glide-0.13.1/docs/importing.md000066400000000000000000000012231320041442700161350ustar00rootroot00000000000000# 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 ``` glide-0.13.1/docs/index.md000066400000000000000000000032131320041442700152350ustar00rootroot00000000000000# 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. glide-0.13.1/docs/plugins.md000066400000000000000000000055601320041442700156160ustar00rootroot00000000000000# 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" ``` glide-0.13.1/docs/resolving-imports.md000066400000000000000000000057671320041442700176510ustar00rootroot00000000000000# 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. glide-0.13.1/docs/vendor.md000066400000000000000000000054241320041442700154310ustar00rootroot00000000000000# 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. glide-0.13.1/docs/versions.md000066400000000000000000000040411320041442700157760ustar00rootroot00000000000000# 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` glide-0.13.1/gb/000077500000000000000000000000001320041442700132455ustar00rootroot00000000000000glide-0.13.1/gb/gb.go000066400000000000000000000030001320041442700141550ustar00rootroot00000000000000package 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 } glide-0.13.1/gb/manifest.go000066400000000000000000000011601320041442700154000ustar00rootroot00000000000000// 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"` } glide-0.13.1/glide.go000066400000000000000000000637111320041442700143000ustar00rootroot00000000000000// 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.0-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 } glide-0.13.1/glide.lock000066400000000000000000000010761320041442700146170ustar00rootroot00000000000000hash: 1f13d16b2759f4c698bf1fa66a55ef06a42ba86859153c478f903e60502a1273 updated: 2017-10-04T10:27:41.570512797-04:00 imports: - name: github.com/codegangsta/cli version: cfb38830724cc34fedffe9a2a29fb54fa9169cd1 - name: github.com/Masterminds/semver version: 15d8430ab86497c5c0da827b748823945e1cf1e1 - name: github.com/Masterminds/vcs version: 6f1c6d150500e452704e9863f68c2559f58616bf - name: github.com/mitchellh/go-homedir version: b8bc1bf767474819792c23f32d8286a45736f1c6 - name: gopkg.in/yaml.v2 version: a3f3340b5840cee44f372bddb5880fcbc419b46a testImports: [] glide-0.13.1/glide.yaml000066400000000000000000000010051320041442700146210ustar00rootroot00000000000000package: 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.12.0 - package: github.com/codegangsta/cli version: ^1.16.0 - package: github.com/Masterminds/semver version: ^1.4.0 - package: github.com/mitchellh/go-homedir glide-0.13.1/glide_test.go000066400000000000000000000002201320041442700153210ustar00rootroot00000000000000package main import ( "testing" ) func TestCommandsNonEmpty(t *testing.T) { commands := commands() if len(commands) == 0 { t.Fail() } } glide-0.13.1/godep/000077500000000000000000000000001320041442700137535ustar00rootroot00000000000000glide-0.13.1/godep/godep.go000066400000000000000000000057161320041442700154110ustar00rootroot00000000000000// 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 } glide-0.13.1/godep/strip/000077500000000000000000000000001320041442700151145ustar00rootroot00000000000000glide-0.13.1/godep/strip/strip.go000066400000000000000000000065261320041442700166150ustar00rootroot00000000000000// 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) return os.RemoveAll(path) } 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/") } glide-0.13.1/godep/strip/strip_test.go000066400000000000000000000007001320041442700176400ustar00rootroot00000000000000package 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) } } } glide-0.13.1/gom/000077500000000000000000000000001320041442700134375ustar00rootroot00000000000000glide-0.13.1/gom/gom.go000066400000000000000000000047031320041442700145540ustar00rootroot00000000000000package 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{} } glide-0.13.1/gom/parser.go000066400000000000000000000060431320041442700152650ustar00rootroot00000000000000package 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}) } } glide-0.13.1/gpm/000077500000000000000000000000001320041442700134405ustar00rootroot00000000000000glide-0.13.1/gpm/gpm.go000066400000000000000000000030141320041442700145500ustar00rootroot00000000000000// 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 } glide-0.13.1/importer/000077500000000000000000000000001320041442700145165ustar00rootroot00000000000000glide-0.13.1/importer/importer.go000066400000000000000000000043451320041442700167140ustar00rootroot00000000000000// 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 } glide-0.13.1/mirrors/000077500000000000000000000000001320041442700143525ustar00rootroot00000000000000glide-0.13.1/mirrors/cfg.go000066400000000000000000000045701320041442700154460ustar00rootroot00000000000000package 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"` } glide-0.13.1/mirrors/mirrors.go000066400000000000000000000025261320041442700164030ustar00rootroot00000000000000// 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 } glide-0.13.1/mirrors/mirrors_test.go000066400000000000000000000013051320041442700174340ustar00rootroot00000000000000package 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") } } glide-0.13.1/mkdocs.yml000066400000000000000000000005341320041442700146620ustar00rootroot00000000000000site_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 glide-0.13.1/msg/000077500000000000000000000000001320041442700134435ustar00rootroot00000000000000glide-0.13.1/msg/msg.go000066400000000000000000000201451320041442700145620ustar00rootroot00000000000000package 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() } glide-0.13.1/msg/out.go000066400000000000000000000012051320041442700145770ustar00rootroot00000000000000// +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) } glide-0.13.1/msg/out_windows.go000066400000000000000000000007571320041442700163640ustar00rootroot00000000000000// +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 } glide-0.13.1/path/000077500000000000000000000000001320041442700136115ustar00rootroot00000000000000glide-0.13.1/path/path.go000066400000000000000000000161721320041442700151030ustar00rootroot00000000000000// 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 } glide-0.13.1/path/path_test.go000066400000000000000000000057131320041442700161410ustar00rootroot00000000000000package 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") } } glide-0.13.1/path/strip.go000066400000000000000000000016561320041442700153110ustar00rootroot00000000000000package path import ( "os" "path/filepath" "github.com/Masterminds/glide/godep/strip" "github.com/Masterminds/glide/msg" ) // 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, func(path string, info os.FileInfo, err error) error { // Skip the base vendor directory if path == searchPath { return nil } name := info.Name() if name == "vendor" { if _, err := os.Stat(path); err == nil { if info.IsDir() { msg.Info("Removing: %s", path) return CustomRemoveAll(path) } msg.Debug("%s is not a directory. Skipping removal", path) return nil } } return nil }) if err != nil { return err } return strip.GodepWorkspace(searchPath) } glide-0.13.1/path/winbug.go000066400000000000000000000063221320041442700154360ustar00rootroot00000000000000package 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 } glide-0.13.1/repo/000077500000000000000000000000001320041442700136225ustar00rootroot00000000000000glide-0.13.1/repo/installer.go000066400000000000000000000650351320041442700161570ustar00rootroot00000000000000package 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] } glide-0.13.1/repo/repo.go000066400000000000000000000012301320041442700151120ustar00rootroot00000000000000// 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 glide-0.13.1/repo/semver.go000066400000000000000000000013611320041442700154530ustar00rootroot00000000000000package 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 } glide-0.13.1/repo/set_reference.go000066400000000000000000000035321320041442700167650ustar00rootroot00000000000000package 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 } glide-0.13.1/repo/tracker.go000066400000000000000000000015541320041442700156110ustar00rootroot00000000000000package 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() } glide-0.13.1/repo/tracker_test.go000066400000000000000000000007721320041442700166510ustar00rootroot00000000000000package 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") } } glide-0.13.1/repo/vcs.go000066400000000000000000000305061320041442700147500ustar00rootroot00000000000000package 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 } glide-0.13.1/testdata/000077500000000000000000000000001320041442700144665ustar00rootroot00000000000000glide-0.13.1/testdata/name/000077500000000000000000000000001320041442700154065ustar00rootroot00000000000000glide-0.13.1/testdata/name/glide.yaml000066400000000000000000000000531320041442700173540ustar00rootroot00000000000000package: technosophos.com/x/foo import: [] glide-0.13.1/testdata/name/glide2.yaml000066400000000000000000000000411320041442700174330ustar00rootroot00000000000000package: another/name import: [] glide-0.13.1/testdata/nv/000077500000000000000000000000001320041442700151115ustar00rootroot00000000000000glide-0.13.1/testdata/nv/a/000077500000000000000000000000001320041442700153315ustar00rootroot00000000000000glide-0.13.1/testdata/nv/a/foo.empty000066400000000000000000000000001320041442700171620ustar00rootroot00000000000000glide-0.13.1/testdata/nv/b/000077500000000000000000000000001320041442700153325ustar00rootroot00000000000000glide-0.13.1/testdata/nv/b/foo.empty000066400000000000000000000000001320041442700171630ustar00rootroot00000000000000glide-0.13.1/testdata/nv/c/000077500000000000000000000000001320041442700153335ustar00rootroot00000000000000glide-0.13.1/testdata/nv/c/foo.empty000066400000000000000000000000001320041442700171640ustar00rootroot00000000000000glide-0.13.1/testdata/path/000077500000000000000000000000001320041442700154225ustar00rootroot00000000000000glide-0.13.1/testdata/path/a/000077500000000000000000000000001320041442700156425ustar00rootroot00000000000000glide-0.13.1/testdata/path/a/b/000077500000000000000000000000001320041442700160635ustar00rootroot00000000000000glide-0.13.1/testdata/path/a/b/c/000077500000000000000000000000001320041442700163055ustar00rootroot00000000000000glide-0.13.1/testdata/path/a/b/c/placeholder.empty000066400000000000000000000000001320041442700216350ustar00rootroot00000000000000glide-0.13.1/testdata/path/a/glide.yaml000066400000000000000000000000001320041442700176000ustar00rootroot00000000000000glide-0.13.1/testdata/path/x/000077500000000000000000000000001320041442700156715ustar00rootroot00000000000000glide-0.13.1/testdata/path/x/glide.yaml000066400000000000000000000000001320041442700176270ustar00rootroot00000000000000glide-0.13.1/testdata/path/x/symlinked_vendor/000077500000000000000000000000001320041442700212455ustar00rootroot00000000000000glide-0.13.1/testdata/path/x/symlinked_vendor/placeholder.empty000066400000000000000000000000001320041442700245750ustar00rootroot00000000000000glide-0.13.1/testdata/path/x/vendor000077700000000000000000000000001320041442700224032symlinked_vendorustar00rootroot00000000000000glide-0.13.1/testdata/path/x/y/000077500000000000000000000000001320041442700161415ustar00rootroot00000000000000glide-0.13.1/testdata/path/x/y/z/000077500000000000000000000000001320041442700164125ustar00rootroot00000000000000glide-0.13.1/testdata/path/x/y/z/placeholder.empty000066400000000000000000000000001320041442700217420ustar00rootroot00000000000000glide-0.13.1/testdata/plugin/000077500000000000000000000000001320041442700157645ustar00rootroot00000000000000glide-0.13.1/testdata/plugin/glide-hello000077500000000000000000000000561320041442700201000ustar00rootroot00000000000000#!/bin/bash echo "Hello from the other glide" glide-0.13.1/testdata/plugin/glide-hello-win.bat000066400000000000000000000000541320041442700214330ustar00rootroot00000000000000@echo off echo "Hello from the other glide" glide-0.13.1/testdata/rebuild/000077500000000000000000000000001320041442700161145ustar00rootroot00000000000000glide-0.13.1/testdata/rebuild/glide.yaml000066400000000000000000000001351320041442700200630ustar00rootroot00000000000000package: github.com/Masterminds/glide/testdata/plugin import: - package: example.com/x/foo glide-0.13.1/tree/000077500000000000000000000000001320041442700136145ustar00rootroot00000000000000glide-0.13.1/tree/tree.go000066400000000000000000000132671320041442700151130ustar00rootroot00000000000000package 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 } glide-0.13.1/tree/tree_test.go000066400000000000000000000017431320041442700161460ustar00rootroot00000000000000/* 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") } } glide-0.13.1/util/000077500000000000000000000000001320041442700136325ustar00rootroot00000000000000glide-0.13.1/util/normalizename_test.go000066400000000000000000000021421320041442700200600ustar00rootroot00000000000000package 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) } } } glide-0.13.1/util/util.go000066400000000000000000000223041320041442700151370ustar00rootroot00000000000000package 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 } glide-0.13.1/util/util_test.go000066400000000000000000000046361320041442700162060ustar00rootroot00000000000000package 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) } } }