original version
This commit is contained in:
commit
6cfe2d1bf4
|
@ -0,0 +1,25 @@
|
|||
# Please do not submit patches for including directives to ignore IDE/editor
|
||||
# generated files. Use a global gitignore as described in
|
||||
# https://help.github.com/articles/ignoring-files and find useful gitignore
|
||||
# samples at https://github.com/github/gitignore
|
||||
|
||||
# system crap
|
||||
.DS_Store
|
||||
.*.swp
|
||||
|
||||
# files created by running the specs
|
||||
tmp/
|
||||
|
||||
# built gems
|
||||
pkg/
|
||||
*.gem
|
||||
|
||||
# rubinius bytecode
|
||||
*.rbc
|
||||
.rbx/
|
||||
|
||||
# output from ronn
|
||||
lib/bundler/man/
|
||||
|
||||
# output from ci_reporter
|
||||
spec/reports/
|
|
@ -0,0 +1,2 @@
|
|||
--format documentation
|
||||
--color
|
|
@ -0,0 +1,96 @@
|
|||
language: ruby
|
||||
script: rake spec:travis
|
||||
before_script: travis_retry rake spec:travis:deps
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- 1-7-stable
|
||||
- 1-6-stable
|
||||
- 1-5-stable
|
||||
- 1-3-stable
|
||||
- 1-2-stable
|
||||
- 1-1-stable
|
||||
- 1-0-stable
|
||||
notifications:
|
||||
email:
|
||||
# andre
|
||||
- secure: "bCcvqJT7YrBawtkXXwHhT+jOFth7r2Qv/30PkkbhQxk6Jb3xambjCOJ3U6vJ\ngYmiL50exi5lUp3oc3SEbHN5t2CrZqOZDQ6o7P8EAmB5c0oH2RrYaFOkI5Gt\nul/jGH/96A9sj0aMwG7JfdMSfhqj1DUKAm2PnnbXPL853VfmT24="
|
||||
# terence
|
||||
- secure: "MQ8eA5Jb8YzEpAo58DRGfVJklAPcEbAulpBZnTxp0am6ldneDtJHbQk21w6R\nj5GsDHlzr/lMp/GHIimtUZ7rLohfND8fj/W7fs1Dkd4eN02/ERt98x3pHlqv\nvZgSnZ39uVYv+OcphraE24QaRaGWLhWZAMYQTVe/Yz50NyG8g1U="
|
||||
campfire:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
rooms:
|
||||
# Bundler Ops
|
||||
secure: MNTSGIySYwHia5gIgRiZxXtPMPDIP9KmzQk7Kq2ZoVvP3mIk8W1TMkvcyFkEf6uCasyVZZixzUBfY+E0BlHAz1ycQpTh1jvSpuIpEVYW48ShJldJ+8W8xfzafyOHii3z7VrDaomEffmMDdmHRsbQAfekMjdR4bTpXtT9V+wOXlg=
|
||||
rvm:
|
||||
- 2.1.1
|
||||
- 2.0.0
|
||||
- 1.9.3
|
||||
- 1.8.7
|
||||
# Rubygems versions MUST be available as rake tasks
|
||||
# see Rakefile:66 for the list of possible RGV values
|
||||
env:
|
||||
# We need to know if changes to rubygems will break bundler on release
|
||||
- RGV=master
|
||||
# Test the latest rubygems release with all of our supported rubies
|
||||
- RGV=v2.2.2
|
||||
matrix:
|
||||
include:
|
||||
# Ruby 2.0.0, Rubygems 2.0 and up
|
||||
- rvm: 2.0.0
|
||||
env: RGV=v2.1.11
|
||||
- rvm: 2.0.0
|
||||
env: RGV=v2.0.14
|
||||
# Ruby 1.9.3, Rubygems 1.5.3 and up
|
||||
- rvm: 1.9.3
|
||||
env: RGV=v2.1.11
|
||||
- rvm: 1.9.3
|
||||
env: RGV=v2.0.14
|
||||
- rvm: 1.9.3
|
||||
env: RGV=v1.8.29
|
||||
- rvm: 1.9.3
|
||||
env: RGV=v1.7.2
|
||||
- rvm: 1.9.3
|
||||
env: RGV=v1.6.2
|
||||
- rvm: 1.9.3
|
||||
env: RGV=v1.5.3
|
||||
# Ruby 1.8.7, Rubygems 1.3.6 and up
|
||||
- rvm: 1.8.7
|
||||
env: RGV=v2.1.11
|
||||
- rvm: 1.8.7
|
||||
env: RGV=v2.0.14
|
||||
- rvm: 1.8.7
|
||||
env: RGV=v1.8.29
|
||||
- rvm: 1.8.7
|
||||
env: RGV=v1.7.2
|
||||
- rvm: 1.8.7
|
||||
env: RGV=v1.6.2
|
||||
- rvm: 1.8.7
|
||||
env: RGV=v1.5.3
|
||||
- rvm: 1.8.7
|
||||
env: RGV=v1.4.2
|
||||
- rvm: 1.8.7
|
||||
env: RGV=v1.3.7
|
||||
- rvm: 1.8.7
|
||||
env: RGV=v1.3.6
|
||||
|
||||
# ALLOWED FAILURES
|
||||
# Ruby 1.9.2 sanity check
|
||||
# (but it's just too slow and sometimes goes over the Travis limit)
|
||||
- rvm: 1.9.2
|
||||
env: RGV=v2.2.2
|
||||
# Ruby-head (we want to know how we're doing, but not fail the build)
|
||||
- rvm: ruby-head
|
||||
env: RGV=master
|
||||
# JRuby, the latest (not maintained, but good to know)
|
||||
- rvm: jruby
|
||||
env: RGV=v2.2.2
|
||||
# Rubinius, the latest (not maintained, but good to know)
|
||||
- rvm: rbx
|
||||
env: RGV=v2.2.2
|
||||
allow_failures:
|
||||
- rvm: ruby-head
|
||||
- rvm: 1.9.2
|
||||
- rvm: jruby
|
||||
- rvm: rbx
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,30 @@
|
|||
# Contributing
|
||||
|
||||
Bundler welcomes contributions from *everyone*. While contributing, please follow the project [code of conduct](http://bundler.io/conduct.html), so that everyone can be included.
|
||||
|
||||
Here are some ways you can contribute:
|
||||
|
||||
- by using prerelease versions
|
||||
- by reporting bugs
|
||||
- by suggesting new features
|
||||
- by writing or editing documentation
|
||||
- by closing issues
|
||||
- by reviewing patches
|
||||
- by refactoring code
|
||||
- by writing code (no patch is too small! fix typos or bad whitespace)
|
||||
|
||||
If you'd like to help make Bundler better, you totally rock! Please check out the [DEVELOPMENT](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md) file for an introduction to the project, guidelines for contributing, and details about what would be helpful.
|
||||
|
||||
Thanks for helping us make Bundler better.
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
If you're having a problem, please see [ISSUES](https://github.com/bundler/bundler/blob/master/ISSUES.md) for troubleshooting steps and a guide for how to submit a ticket that will help us solve the problem you are having as quickly as possible.
|
||||
|
||||
# Requesting Features
|
||||
|
||||
Head on over to the [Bundler features](https://github.com/bundler/bundler-features) project, or any of the lists or channels listed below. Feature-wise we consider Bundler stable, so the core team does not tend to work on feature suggestions. Pull requests welcome though!
|
||||
|
||||
# Discussing Bundler
|
||||
|
||||
If you'd like to discuss features, ask questions, or just engage in general Bundler-focused discussion, please see the [#bundler](irc://irc.freenode.net/#bundler) IRC channel on Freenode, and the [Bundler mailing list](http://groups.google.com/group/ruby-bundler) on Google Groups.
|
|
@ -0,0 +1,117 @@
|
|||
Great to have you here! Here are a few ways you can help out with [Bundler](http://github.com/bundler/bundler).
|
||||
|
||||
# Where should I start?
|
||||
|
||||
You can start learning about Bundler by reading [the documentation](http://bundler.io). If you want, you can also read a (lengthy) explanation of [why Bundler exists and what it does](http://bundler.io/v1.5/rationale.html). You can also check out discussions about Bundler on the [Bundler mailing list](https://groups.google.com/group/ruby-bundler) and in the [Bundler IRC channel](http://webchat.freenode.net/?channels=%23bundler), which is #bundler on Freenode.
|
||||
|
||||
## Your first commits
|
||||
|
||||
If you’re interested in contributing to Bundler, that’s awesome! We’d love your help.
|
||||
|
||||
If you have any questions after reading this page, please feel free to contact either [@indirect](http://github.com/indirect) or [@hone](http://github.com/hone). They are both happy to provide help working through your first bugfix or thinking through the problem you’re trying to resolve.
|
||||
|
||||
## Tackle some small problems
|
||||
|
||||
We track [small
|
||||
bugs](https://github.com/bundler/bundler/issues?labels=small&state=open) and [small features](https://github.com/bundler/bundler-features/issues?labels=small&state=open) so that anyone who wants to help can start with something that's not too overwhelming. If nothing on those lists looks good, though, just talk to us.
|
||||
|
||||
|
||||
# Development setup
|
||||
|
||||
Bundler doesn't use a Gemfile to list development dependencies, because when we tried it we couldn't tell if we were awake or it was just another level of dreams. To work on Bundler, you'll probably want to do a couple of things.
|
||||
|
||||
1. Install Bundler's development dependencies
|
||||
|
||||
$ rake spec:deps
|
||||
|
||||
2. Run the test suite, to make sure things are working
|
||||
|
||||
$ rake spec
|
||||
|
||||
3. Set up a shell alias to run Bundler from your clone, e.g. a Bash alias:
|
||||
|
||||
$ alias dbundle='ruby -I /path/to/bundler/lib /path/to/bundler/bin/bundle'
|
||||
|
||||
With that set up, you can test changes you've made to Bundler by running `dbundle`, without interfering with the regular `bundle` command.
|
||||
|
||||
|
||||
# Bug triage
|
||||
|
||||
Triage is the work of processing tickets that have been opened into actionable issues, feature requests, or bug reports. That includes verifying bugs, categorizing the ticket, and ensuring there's enough information to reproduce the bug for anyone who wants to try to fix it.
|
||||
|
||||
We've created an [issues guide](https://github.com/bundler/bundler/blob/master/ISSUES.md) to walk Bundler users through the process of troubleshooting issues and reporting bugs.
|
||||
|
||||
If you'd like to help, awesome! You can [report a new bug](https://github.com/bundler/bundler/issues/new) or browse our [existing open tickets](https://github.com/bundler/bundler/issues).
|
||||
|
||||
Not every ticket will point to a bug in Bundler's code, but open tickets usually mean that there is something we could improve to help that user. Sometimes that means writing additional documentation, sometimes that means making error messages clearer, and sometimes that means explaining to a user that they need to install git to use git gems.
|
||||
|
||||
When you're looking at a ticket, here are the main questions to ask:
|
||||
|
||||
* Can I reproduce this bug myself?
|
||||
* Are the steps to reproduce clearly stated in the ticket?
|
||||
* Which versions of Bundler (1.1.x, 1.2.x, git, etc.) manifest this bug?
|
||||
* Which operating systems (OS X, Windows, Ubuntu, CentOS, etc.) manifest this bug?
|
||||
* Which rubies (MRI, JRuby, Rubinius, etc.) and which versions (1.8.7, 1.9.3, etc.) have this bug?
|
||||
|
||||
If you can't reproduce an issue, chances are good that the bug has been fixed (hurrah!). That's a good time to post to the ticket explaining what you did and how it worked.
|
||||
|
||||
If you can reproduce an issue, you're well on your way to fixing it. :) Fixing issues is similar to adding new features:
|
||||
|
||||
1. Discuss the fix on the existing issue. Coordinating with everyone else saves duplicate work and serves as a great way to get suggestions and ideas if you need any.
|
||||
2. Base your commits on the correct branch. Bugfixes for 1.x versions of Bundler should be based on the matching 1-x-stable branch.
|
||||
3. Commit the code and at least one test covering your changes to a named branch in your fork.
|
||||
4. Put a line in the [CHANGELOG](https://github.com/bundler/bundler/blob/master/CHANGELOG.md) summarizing your changes under the next release under the “Bugfixes” heading.
|
||||
5. Send us a [pull request](https://help.github.com/articles/using-pull-requests) from your bugfix branch.
|
||||
|
||||
Finally, the ticket may be a duplicate of another older ticket. If you notice a ticket is a duplicate, simply comment on the ticket noting the original ticket’s number. For example, you could say “This is a duplicate of issue #42, and can be closed”.
|
||||
|
||||
|
||||
# Adding new features
|
||||
|
||||
If you would like to add a new feature to Bundler, please follow these steps:
|
||||
|
||||
1. [Create an issue](https://github.com/bundler/bundler-features/issues/new) to discuss your feature.
|
||||
2. Base your commits on the master branch, since we follow [SemVer](http://semver.org) and don't add new features to old releases.
|
||||
3. Commit the code and at least one test covering your changes to a feature branch in your fork.
|
||||
4. Put a line in the [CHANGELOG](https://github.com/bundler/bundler/blob/master/CHANGELOG.md) summarizing your changes under the next release under the "Features" heading.
|
||||
5. Send us a [pull request](https://help.github.com/articles/using-pull-requests) from your feature branch.
|
||||
|
||||
If you don't hear back immediately, don’t get discouraged! We all have day jobs, but we respond to most tickets within a day or two.
|
||||
|
||||
|
||||
# Beta testing
|
||||
|
||||
Early releases require heavy testing, especially across various system setups. We :heart: testers, and are big fans of anyone who can run `gem install bundler --pre` and try out upcoming releases in their development and staging environments.
|
||||
|
||||
There may not always be prereleases or beta versions of Bundler. That said, you are always welcome to try checking out master and building a gem yourself if you want to try out the latest changes.
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
We don't currently have any translations, but please reach out to us if you would like to help get this going.
|
||||
|
||||
|
||||
# Documentation
|
||||
|
||||
Code needs explanation, and sometimes those who know the code well have trouble explaining it to someone just getting into it. Because of that, we welcome documentation suggestions and patches from everyone, especially if they are brand new to using Bundler.
|
||||
|
||||
Bundler has two main sources of documentation: the built-in help (including usage information and man pages) and the [Bundler documentation site](http://bundler.io).
|
||||
|
||||
If you’d like to submit a patch to the man pages, follow the steps for adding a feature above. All of the man pages are located in the `man` directory. Just use the “Documentation” heading when you describe what you did in the changelog.
|
||||
|
||||
If you have a suggestion or proposed change for [bundler.io](http://bundler.io), please open an issue or send a pull request to the [bundler-site](https://github.com/bundler/bundler-site) repository.
|
||||
|
||||
|
||||
# Community
|
||||
|
||||
Community is an important part of all we do. If you’d like to be part of the Bundler community, you can jump right in and start helping make Bundler better for everyone who uses it.
|
||||
|
||||
It would be tremendously helpful to have more people answering questions about Bundler (and often simply about Rubygems or Ruby itself) in our [issue tracker](https://github.com/bundler/bundler/issues) or on [Stack Overflow](http://stackoverflow.com/questions/tagged/bundler).
|
||||
|
||||
Additional documentation and explanation is always helpful, too. If you have any suggestions for the Bundler website [bundler.io](http://bundler.io), we would absolutely love it if you opened an issue or pull request on the [bundler-site](https://github.com/bundler/bundler-site) repository.
|
||||
|
||||
Finally, sharing your experiences and discoveries by writing them up is a valuable way to help others who have similar problems or experiences in the future. You can write a blog post, create an example and commit it to Github, take screenshots, or make videos.
|
||||
|
||||
Examples of how Bundler is used help everyone, and we’ve discovered that people already use it in ways that we never imagined when we were writing it. If you’re still not sure what to write about, there are also several projects doing interesting things based on Bundler. They could probably use publicity too.
|
||||
|
||||
If you let someone on the core team know you wrote about Bundler, we will add your post to the list of Bundler resources on the Github project wiki.
|
|
@ -0,0 +1,96 @@
|
|||
# Bundler Issues
|
||||
|
||||
So! You're having problems with Bundler. This file is here to help. If you're running into an error, try reading the rest of this file for help. If you can't figure out how to solve your problem, there are also instructions on how to report a bug.
|
||||
|
||||
**Please use the [Bundler
|
||||
Features](https://github.com/bundler/bundler-features) repo to suggest and
|
||||
discuss features. The bundler issue tracker is only for bugs.**
|
||||
|
||||
## Documentation
|
||||
|
||||
Instructions for common Bundler uses can be found on the [Bundler documentation site](http://bundler.io/).
|
||||
|
||||
Detailed information about each Bundler command, including help with common problems, can be found in the [Bundler man pages](http://bundler.io/v1.3/man/bundle.1.html).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Heroku errors
|
||||
|
||||
Please open a ticket with Heroku if you're having trouble deploying. They have a professional support team who can help you resolve Heroku issues far better than the Bundler team can. If the problem that you are having turns out to be a bug in Bundler itself, Heroku support can get the exact details to us.
|
||||
|
||||
### Other problems
|
||||
|
||||
First, figure out exactly what it is that you're trying to do. Then, go to the [Bundler documentation website](http://bundler.io) and see if we have instructions on how to do that.
|
||||
|
||||
Second, check [the compatibility
|
||||
list](http://bundler.io/compatibility.html), and make sure that the version of Bundler that you are
|
||||
using works with the versions of Ruby and Rubygems that you are using.
|
||||
|
||||
If the instructions don't work, or you can't find any instructions, you can try these troubleshooting steps:
|
||||
|
||||
# remove user-specific gems and git repos
|
||||
rm -rf ~/.bundle/ ~/.gem/bundler/ ~/.gems/cache/bundler/
|
||||
|
||||
# remove system-wide git repos and git checkouts
|
||||
rm -rf $GEM_HOME/bundler/ $GEM_HOME/cache/bundler/
|
||||
|
||||
# remove project-specific settings
|
||||
rm -rf .bundle/
|
||||
|
||||
# remove project-specific cached gems and repos
|
||||
rm -rf vendor/cache/
|
||||
|
||||
# remove the saved resolve of the Gemfile
|
||||
rm -rf Gemfile.lock
|
||||
|
||||
# uninstall the rubygems-bundler and open_gem gems
|
||||
rvm gemset use global # if using rvm
|
||||
gem uninstall rubygems-bundler open_gem
|
||||
|
||||
# try to install one more time
|
||||
bundle install
|
||||
|
||||
## Reporting unresolved problems
|
||||
|
||||
Hopefully the troubleshooting steps above resolved your problem. If things still aren't working the way you expect them to, please let us know so that we can diagnose and hopefully fix the problem you're having.
|
||||
|
||||
**The best way to report a bug is by providing a reproduction script.** See these examples:
|
||||
|
||||
* [Git environment variables causing install to fail.](https://gist.github.com/xaviershay/6207550)
|
||||
* [Multiple gems in a repository cannot be updated independently.](https://gist.github.com/xaviershay/6295889)
|
||||
|
||||
A half working script with comments for the parts you were unable to automate is still appreciated.
|
||||
|
||||
If you are unable to do that, please include the following information in your report:
|
||||
|
||||
- What you're trying to accomplish
|
||||
- The command you ran
|
||||
- What you expected to happen
|
||||
- What actually happened
|
||||
- The exception backtrace(s), if any
|
||||
- Everything output by running `bundle env`
|
||||
|
||||
If your version of Bundler does not have the `bundle env` command, then please include:
|
||||
|
||||
- Your Gemfile
|
||||
- Your Gemfile.lock
|
||||
- Your Bundler configuration settings (run `bundle config`)
|
||||
- What version of bundler you are using (run `bundle -v`)
|
||||
- What version of Ruby you are using (run `ruby -v`)
|
||||
- What version of Rubygems you are using (run `gem -v`)
|
||||
- Whether you are using RVM, and if so what version (run `rvm -v`)
|
||||
- Whether you have the `rubygems-bundler` gem, which can break gem executables (run `gem list rubygems-bundler`)
|
||||
- Whether you have the `open_gem` gem, which can cause rake activation conflicts (run `gem list open_gem`)
|
||||
|
||||
If you are using Rails 2.3, please also include:
|
||||
|
||||
- Your boot.rb file
|
||||
- Your preinitializer.rb file
|
||||
- Your environment.rb file
|
||||
|
||||
|
||||
If you have either `rubygems-bundler` or `open_gem` installed, please try removing them and then following the troubleshooting steps above before opening a new ticket.
|
||||
|
||||
[Create a gist](https://gist.github.com) containing all of that information, then visit the [Bundler issue tracker](https://github.com/bundler/bundler/issues) and [create a ticket](https://github.com/bundler/bundler/issues/new) describing your problem and linking to your gist.
|
||||
|
||||
Thanks for reporting issues and helping make Bundler better!
|
|
@ -0,0 +1,23 @@
|
|||
Portions copyright (c) 2010 Andre Arko
|
||||
Portions copyright (c) 2009 Engine Yard
|
||||
|
||||
MIT License
|
||||
|
||||
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.
|
|
@ -0,0 +1,40 @@
|
|||
[![Code Climate](https://img.shields.io/codeclimate/github/bundler/bundler.svg)](https://codeclimate.com/github/bundler/bundler)
|
||||
[![Build Status](https://img.shields.io/travis/bundler/bundler/master.svg)](https://travis-ci.org/bundler/bundler)
|
||||
[![Version ](https://img.shields.io/gem/v/bundler.svg)](https://rubygems.org/gems/bundler)
|
||||
|
||||
# Bundler: a gem to bundle gems
|
||||
Bundler keeps ruby applications running the same code on every machine.
|
||||
|
||||
It does this by managing the gems that the application depends on. Given a list of gems, it can automatically download and install those gems, as well as any other gems needed by the gems that are listed. Before installing gems, it checks the versions of every gem to make sure that they are compatible, and can all be loaded at the same time. After the gems have been installed, Bundler can help you update some or all of them when new versions become available. Finally, it records the exact versions that have been installed, so that others can install the exact same gems.
|
||||
|
||||
### Installation and usage
|
||||
|
||||
```
|
||||
gem install bundler
|
||||
bundle init
|
||||
echo "gem 'rails'" >> Gemfile
|
||||
bundle install
|
||||
bundle exec rails new myapp
|
||||
```
|
||||
|
||||
See [bundler.io](http://bundler.io) for the full documentation.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
For help with common problems, see [ISSUES](https://github.com/bundler/bundler/blob/master/ISSUES.md).
|
||||
|
||||
### Contributing
|
||||
|
||||
If you'd like to contribute to Bundler, that's awesome, and we <3 you. There's a guide to contributing to Bundler (both code and general help) over in [DEVELOPMENT](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md)
|
||||
|
||||
The `master` branch contains our current progress towards version 1.5. Versions 1.0-1.3 each have their own stable branches. Please submit bugfixes as pull requests to the stable branch for the version you would like to fix.
|
||||
|
||||
### Core Team
|
||||
|
||||
The Bundler core team consists of André Arko ([@indirect](http://github.com/indirect)), Terence Lee ([@hone](http://github.com/hone)), and Jessica Lynn Suttles ([@jlsuttles](http://github.com/jlsuttles)), with support and advice from original Bundler author Yehuda Katz ([@wycats](http://github.com/wycats)).
|
||||
|
||||
### Other questions
|
||||
|
||||
To see what has changed in recent versions of Bundler, see the [CHANGELOG](https://github.com/bundler/bundler/blob/master/CHANGELOG.md).
|
||||
|
||||
Feel free to chat with the Bundler core team (and many other users) on IRC in the [#bundler](irc://irc.freenode.net/bundler) channel on Freenode, or via email on the [Bundler mailing list](http://groups.google.com/group/ruby-bundler).
|
|
@ -0,0 +1,256 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
$:.unshift File.expand_path("../lib", __FILE__)
|
||||
require 'rubygems'
|
||||
require 'shellwords'
|
||||
require 'benchmark'
|
||||
|
||||
RUBYGEMS_REPO = File.expand_path("tmp/rubygems")
|
||||
|
||||
def safe_task(&block)
|
||||
yield
|
||||
true
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
# Benchmark task execution
|
||||
module Rake
|
||||
class Task
|
||||
alias_method :real_invoke, :invoke
|
||||
|
||||
def invoke(*args)
|
||||
time = Benchmark.measure(@name) do
|
||||
real_invoke(*args)
|
||||
end
|
||||
puts "#{@name} ran for #{time}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
namespace :spec do
|
||||
desc "Ensure spec dependencies are installed"
|
||||
task :deps do
|
||||
spec = Gem::Specification.load("bundler.gemspec")
|
||||
deps = Hash[spec.development_dependencies.map do |d|
|
||||
[d.name, d.requirement.to_s]
|
||||
end]
|
||||
|
||||
# JRuby can't build ronn or rdiscount, so we skip that
|
||||
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
||||
deps.delete("ronn")
|
||||
deps.delete("rdiscount")
|
||||
end
|
||||
|
||||
deps.each do |name, version|
|
||||
sh "#{Gem.ruby} -S gem list -i '^#{name}$' -v '#{version}' || " \
|
||||
"#{Gem.ruby} -S gem install #{name} -v '#{version}' --no-ri --no-rdoc"
|
||||
end
|
||||
|
||||
# Download and install gems used inside tests
|
||||
$LOAD_PATH.unshift("./spec")
|
||||
require 'support/rubygems_ext'
|
||||
Spec::Rubygems.setup
|
||||
end
|
||||
|
||||
namespace :travis do
|
||||
task :deps do
|
||||
# Give the travis user a name so that git won't fatally error
|
||||
system "sudo sed -i 's/1000::/1000:Travis:/g' /etc/passwd"
|
||||
# Strip secure_path so that RVM paths transmit through sudo -E
|
||||
system "sudo sed -i '/secure_path/d' /etc/sudoers"
|
||||
# Install groff for the ronn gem
|
||||
sh "sudo apt-get install groff -y"
|
||||
if RUBY_VERSION < '1.9'
|
||||
# Downgrade Rubygems on 1.8 so Ronn can be required
|
||||
# https://github.com/rubygems/rubygems/issues/784
|
||||
sh "gem update --system 2.1.11"
|
||||
else
|
||||
# Downgrade Rubygems so RSpec 3 can be instaled
|
||||
# https://github.com/rubygems/rubygems/issues/813
|
||||
sh "gem update --system 2.2.0"
|
||||
end
|
||||
# Install the other gem deps, etc.
|
||||
Rake::Task["spec:deps"].invoke
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require 'rspec/core/rake_task'
|
||||
|
||||
desc "Run specs"
|
||||
RSpec::Core::RakeTask.new do |t|
|
||||
t.rspec_opts = %w(--format documentation --color)
|
||||
t.ruby_opts = %w(-w)
|
||||
end
|
||||
task :spec => "man:build"
|
||||
|
||||
namespace :spec do
|
||||
task :clean do
|
||||
rm_rf 'tmp'
|
||||
end
|
||||
|
||||
desc "Run the real-world spec suite (requires internet)"
|
||||
task :realworld => ["set_realworld", "spec"]
|
||||
|
||||
task :set_realworld do
|
||||
ENV['BUNDLER_REALWORLD_TESTS'] = '1'
|
||||
end
|
||||
|
||||
desc "Run the spec suite with the sudo tests"
|
||||
task :sudo => ["set_sudo", "spec", "clean_sudo"]
|
||||
|
||||
task :set_sudo do
|
||||
ENV['BUNDLER_SUDO_TESTS'] = '1'
|
||||
end
|
||||
|
||||
task :clean_sudo do
|
||||
puts "Cleaning up sudo test files..."
|
||||
system "sudo rm -rf #{File.expand_path('../tmp/sudo_gem_home', __FILE__)}"
|
||||
end
|
||||
|
||||
# Rubygems specs by version
|
||||
namespace :rubygems do
|
||||
rubyopt = ENV["RUBYOPT"]
|
||||
# When editing this list, also edit .travis.yml!
|
||||
branches = %w(master 2.2)
|
||||
releases = %w(v1.3.6 v1.3.7 v1.4.2 v1.5.3 v1.6.2 v1.7.2 v1.8.29 v2.0.14 v2.1.11 v2.2.2)
|
||||
(branches + releases).each do |rg|
|
||||
desc "Run specs with Rubygems #{rg}"
|
||||
RSpec::Core::RakeTask.new(rg) do |t|
|
||||
t.rspec_opts = %w(--format documentation --color)
|
||||
t.ruby_opts = %w(-w)
|
||||
end
|
||||
|
||||
# Create tasks like spec:rubygems:v1.8.3:sudo to run the sudo specs
|
||||
namespace rg do
|
||||
task :sudo => ["set_sudo", rg, "clean_sudo"]
|
||||
task :realworld => ["set_realworld", rg]
|
||||
end
|
||||
|
||||
task "clone_rubygems_#{rg}" do
|
||||
unless File.directory?("tmp/rubygems")
|
||||
system("git clone https://github.com/rubygems/rubygems.git tmp/rubygems")
|
||||
end
|
||||
hash = nil
|
||||
|
||||
Dir.chdir(RUBYGEMS_REPO) do
|
||||
system("git remote update")
|
||||
if rg == "master"
|
||||
system("git checkout origin/master")
|
||||
else
|
||||
system("git checkout #{rg}") || raise("Unknown Rubygems ref #{rg}")
|
||||
end
|
||||
hash = `git rev-parse HEAD`.chomp
|
||||
end
|
||||
|
||||
puts "Checked out rubygems '#{rg}' at #{hash}"
|
||||
ENV["RUBYOPT"] = "-I#{File.expand_path("tmp/rubygems/lib")} #{rubyopt}"
|
||||
puts "RUBYOPT=#{ENV['RUBYOPT']}"
|
||||
end
|
||||
|
||||
task rg => ["man:build", "clone_rubygems_#{rg}"]
|
||||
task "rubygems:all" => rg
|
||||
end
|
||||
|
||||
desc "Run specs under a Rubygems checkout (set RG=path)"
|
||||
RSpec::Core::RakeTask.new("co") do |t|
|
||||
t.rspec_opts = %w(--format documentation --color)
|
||||
t.ruby_opts = %w(-w)
|
||||
end
|
||||
|
||||
task "setup_co" do
|
||||
ENV["RUBYOPT"] = "-I#{File.expand_path ENV['RG']} #{rubyopt}"
|
||||
end
|
||||
|
||||
task "co" => "setup_co"
|
||||
task "rubygems:all" => "co"
|
||||
end
|
||||
|
||||
desc "Run the tests on Travis CI against a rubygem version (using ENV['RGV'])"
|
||||
task :travis do
|
||||
rg = ENV['RGV'] || raise("Rubygems version is required on Travis!")
|
||||
|
||||
puts "\n\e[1;33m[Travis CI] Running bundler specs against rubygems #{rg}\e[m\n\n"
|
||||
specs = safe_task { Rake::Task["spec:rubygems:#{rg}"].invoke }
|
||||
|
||||
Rake::Task["spec:rubygems:#{rg}"].reenable
|
||||
|
||||
puts "\n\e[1;33m[Travis CI] Running bundler sudo specs against rubygems #{rg}\e[m\n\n"
|
||||
sudos = system("sudo -E rake spec:rubygems:#{rg}:sudo")
|
||||
# clean up by chowning the newly root-owned tmp directory back to the travis user
|
||||
system("sudo chown -R #{ENV['USER']} #{File.join(File.dirname(__FILE__), 'tmp')}")
|
||||
|
||||
Rake::Task["spec:rubygems:#{rg}"].reenable
|
||||
|
||||
puts "\n\e[1;33m[Travis CI] Running bundler real world specs against rubygems #{rg}\e[m\n\n"
|
||||
realworld = safe_task { Rake::Task["spec:rubygems:#{rg}:realworld"].invoke }
|
||||
|
||||
{"specs" => specs, "sudo" => sudos, "realworld" => realworld}.each do |name, passed|
|
||||
if passed
|
||||
puts "\e[0;32m[Travis CI] #{name} passed\e[m"
|
||||
else
|
||||
puts "\e[0;31m[Travis CI] #{name} failed\e[m"
|
||||
end
|
||||
end
|
||||
|
||||
unless specs && sudos && realworld
|
||||
fail "Spec run failed, please review the log for more information"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rescue LoadError
|
||||
task :spec do
|
||||
abort "Run `rake spec:deps` to be able to run the specs"
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require 'ronn'
|
||||
|
||||
namespace :man do
|
||||
directory "lib/bundler/man"
|
||||
|
||||
Dir["man/*.ronn"].each do |ronn|
|
||||
basename = File.basename(ronn, ".ronn")
|
||||
roff = "lib/bundler/man/#{basename}"
|
||||
|
||||
file roff => ["lib/bundler/man", ronn] do
|
||||
sh "#{Gem.ruby} -S ronn --roff --pipe #{ronn} > #{roff}"
|
||||
end
|
||||
|
||||
file "#{roff}.txt" => roff do
|
||||
sh "groff -Wall -mtty-char -mandoc -Tascii #{roff} | col -b > #{roff}.txt"
|
||||
end
|
||||
|
||||
task :build_all_pages => "#{roff}.txt"
|
||||
end
|
||||
|
||||
desc "Build the man pages"
|
||||
task :build => "man:build_all_pages"
|
||||
|
||||
desc "Clean up from the built man pages"
|
||||
task :clean do
|
||||
rm_rf "lib/bundler/man"
|
||||
end
|
||||
end
|
||||
|
||||
rescue LoadError
|
||||
namespace :man do
|
||||
task(:build) { warn "Install the ronn gem to be able to release!" }
|
||||
task(:clean) { warn "Install the ronn gem to be able to release!" }
|
||||
end
|
||||
end
|
||||
|
||||
desc "Update vendored SSL certs to match the certs vendored by Rubygems"
|
||||
task :update_certs => "spec:rubygems:clone_rubygems_master" do
|
||||
require 'bundler/ssl_certs/certificate_manager'
|
||||
Bundler::SSLCerts::CertificateManager.update_from!(RUBYGEMS_REPO)
|
||||
end
|
||||
|
||||
require 'bundler/gem_tasks'
|
||||
task :build => ["man:clean", "man:build"]
|
||||
task :release => ["man:clean", "man:build"]
|
||||
|
||||
task :default => :spec
|
|
@ -0,0 +1,103 @@
|
|||
## Bundler 0.9 to 1.0 and above
|
||||
|
||||
Upgrading from Bundler 0.9 to 1.0 is relatively painless. The
|
||||
Gemfile API is the same, so your old Gemfiles should continue
|
||||
to work.
|
||||
|
||||
The "env" file that 0.9 created at `.bundle/environment.rb` has been
|
||||
removed. As a side effect of this, Passenger will only find your
|
||||
bundled gems if you install with `bundle install --deployment`.
|
||||
Alternatively, you can tell Passenger where you gems are installed,
|
||||
[something like this](http://andre.arko.net/2010/08/16/using-passengerpane-with-gem_home-set/).
|
||||
|
||||
The `bundle lock` command is no longer needed, as the
|
||||
Gemfile.lock file is now automatically generated by `bundle install`.
|
||||
If you have not yet done so, add your Gemfile.lock to source control
|
||||
and check it in.
|
||||
|
||||
Running `bundle install` no longer updates the versions of your gems.
|
||||
If you need to update just one gem, run `bundle update GEMNAME`. To
|
||||
update all gems to the newest versions possible, run `bundle update`.
|
||||
|
||||
Bundler now supports multiple platforms, using a block syntax to
|
||||
declare platform-specific gems:
|
||||
|
||||
platform :jruby do
|
||||
gem "jruby-maven-plugins"
|
||||
end
|
||||
|
||||
Deploying using Bundler is even easier than it was before, as Bundler
|
||||
now includes a Capistrano recipe. Simply add this line to the top of
|
||||
your deploy.rb file to run Bundler automatically as part of deploying:
|
||||
|
||||
require 'bundler/capistrano'
|
||||
|
||||
For more details on deploying using bundler, see the documentation
|
||||
for the bundler cap task, and the [documentation on deploying](http://bundler.io/deploying.html).
|
||||
|
||||
|
||||
## Bundler 0.8 to 0.9 and above
|
||||
|
||||
Upgrading to Bundler 0.9 from Bundler 0.8 requires upgrading several
|
||||
API calls in your Gemfile, and some workarounds if you are using Rails 2.3.
|
||||
|
||||
### Gemfile Removals
|
||||
|
||||
Bundler 0.9 removes the following Bundler 0.8 Gemfile APIs:
|
||||
|
||||
1. `disable_system_gems`: This is now the default (and only) option
|
||||
for bundler. Bundler uses the system gems you have specified
|
||||
in the Gemfile, and only the system gems you have specified
|
||||
(and their dependencies)
|
||||
2. `disable_rubygems`: This is no longer supported. We are looking
|
||||
into ways to get the fastest performance out of each supported
|
||||
scenario, and we will make speed the default where possible.
|
||||
3. `clear_sources`: Bundler now defaults to an empty source
|
||||
list. If you want to include Rubygems, you can add the source
|
||||
via source "http://gemcutter.org". If you use bundle init, this
|
||||
source will be automatically added for you in the generated
|
||||
Gemfile
|
||||
4. `bundle_path`: You can specify this setting when installing
|
||||
via `bundle install /path/to/bundle`. Bundler will remember
|
||||
where you installed the dependencies to on a particular
|
||||
machine for future installs, loads, setups, etc.
|
||||
5. `bin_path`: Bundler no longer generates executables in the root
|
||||
of your app. You should use `bundle exec` to execute executables
|
||||
in the current context.
|
||||
|
||||
### Gemfile Changes
|
||||
|
||||
Bundler 0.9 changes the following Bundler 0.8 Gemfile APIs:
|
||||
|
||||
1. Bundler 0.8 supported :only and :except as APIs for describing
|
||||
groups of gems. Bundler 0.9 supports a single `group` method,
|
||||
which you can use to group gems together. See the above "Group"
|
||||
section for more information.
|
||||
|
||||
This means that `gem "foo", :only => :production` becomes
|
||||
`gem "foo", :group => :production`, and
|
||||
`only :production { gem "foo" }` becomes
|
||||
`group :production { gem "foo" }`
|
||||
|
||||
The short version is: group your gems together logically, and
|
||||
use the available commands to make use of the groups you've
|
||||
created.
|
||||
|
||||
2. `:require_as` becomes `:require`
|
||||
|
||||
3. `:vendored_at` is fully removed; you should use `:path`
|
||||
|
||||
### API Changes
|
||||
|
||||
1. `Bundler.require_env(:environment)` becomes
|
||||
`Bundler.require(:multiple, :groups)`. You must
|
||||
now specify the default group (the default group is the
|
||||
group made up of the gems not assigned to any group)
|
||||
explicitly. So `Bundler.require_env(:test)` becomes
|
||||
`Bundler.require(:default, :test)`
|
||||
|
||||
2. `require 'vendor/gems/environment'`: In unlocked
|
||||
mode, where using system gems, this becomes
|
||||
`Bundler.setup(:multiple, :groups)`. If you don't
|
||||
specify any groups, this puts all groups on the load
|
||||
path. In locked mode, it becomes `require '.bundle/environment'`
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# Exit cleanly from an early interrupt
|
||||
Signal.trap("INT") { exit 1 }
|
||||
|
||||
require 'bundler'
|
||||
# Check if an older version of bundler is installed
|
||||
$LOAD_PATH.each do |path|
|
||||
if path =~ %r'/bundler-0.(\d+)' && $1.to_i < 9
|
||||
err = "Looks like you have a version of bundler that's older than 0.9.\n"
|
||||
err << "Please remove your old versions.\n"
|
||||
err << "An easy way to do this is by running `gem cleanup bundler`."
|
||||
abort(err)
|
||||
end
|
||||
end
|
||||
|
||||
require 'bundler/friendly_errors'
|
||||
Bundler.with_friendly_errors do
|
||||
require 'bundler/cli'
|
||||
Bundler::CLI.start(ARGV, :debug => true)
|
||||
end
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
Signal.trap("INT") { exit 1 }
|
||||
|
||||
require 'bundler/ruby_version'
|
||||
require 'bundler/ruby_dsl'
|
||||
require 'bundler/shared_helpers'
|
||||
|
||||
module Bundler
|
||||
class GemfileError < RuntimeError; end
|
||||
class Dsl
|
||||
include RubyDsl
|
||||
|
||||
attr_accessor :ruby_version
|
||||
|
||||
def initialize
|
||||
@ruby_version = nil
|
||||
end
|
||||
|
||||
def eval_gemfile(gemfile, contents = nil)
|
||||
contents ||= File.open(gemfile, "rb") { |f| f.read }
|
||||
instance_eval(contents, gemfile.to_s, 1)
|
||||
rescue SyntaxError => e
|
||||
bt = e.message.split("\n")[1..-1]
|
||||
raise GemfileError, ["Gemfile syntax error:", *bt].join("\n")
|
||||
rescue ScriptError, RegexpError, NameError, ArgumentError => e
|
||||
e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
|
||||
STDERR.puts e.backtrace.join("\n ")
|
||||
raise GemfileError, "There was an error in your Gemfile," \
|
||||
" and Bundler cannot continue."
|
||||
end
|
||||
|
||||
def source(source, options = {})
|
||||
end
|
||||
|
||||
def gem(name, *args)
|
||||
end
|
||||
|
||||
def group(*args, &blk)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dsl = Bundler::Dsl.new
|
||||
begin
|
||||
dsl.eval_gemfile(Bundler::SharedHelpers.default_gemfile)
|
||||
ruby_version = dsl.ruby_version
|
||||
if ruby_version
|
||||
puts ruby_version
|
||||
else
|
||||
puts "No ruby version specified"
|
||||
end
|
||||
rescue Bundler::GemfileError => e
|
||||
puts e.message
|
||||
exit(-1)
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# Exit cleanly from an early interrupt
|
||||
Signal.trap("INT") { exit 1 }
|
||||
|
||||
require 'bundler'
|
||||
# Check if an older version of bundler is installed
|
||||
$LOAD_PATH.each do |path|
|
||||
if path =~ %r'/bundler-0.(\d+)' && $1.to_i < 9
|
||||
err = "Looks like you have a version of bundler that's older than 0.9.\n"
|
||||
err << "Please remove your old versions.\n"
|
||||
err << "An easy way to do this is by running `gem cleanup bundler`."
|
||||
abort(err)
|
||||
end
|
||||
end
|
||||
|
||||
require 'bundler/friendly_errors'
|
||||
Bundler.with_friendly_errors do
|
||||
require 'bundler/cli'
|
||||
Bundler::CLI.start(ARGV, :debug => true)
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
# coding: utf-8
|
||||
lib = File.expand_path('../lib/', __FILE__)
|
||||
$:.unshift lib unless $:.include?(lib)
|
||||
require 'bundler/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = 'bundler'
|
||||
spec.version = Bundler::VERSION
|
||||
spec.licenses = ['MIT']
|
||||
spec.authors = ["André Arko", "Terence Lee", "Carl Lerche", "Yehuda Katz"]
|
||||
spec.email = ["andre@arko.net"]
|
||||
spec.homepage = "http://bundler.io"
|
||||
spec.summary = %q{The best way to manage your application's dependencies}
|
||||
spec.description = %q{Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably}
|
||||
|
||||
spec.required_ruby_version = '>= 1.8.7'
|
||||
spec.required_rubygems_version = '>= 1.3.6'
|
||||
|
||||
spec.add_development_dependency 'rdiscount', '~> 1.6'
|
||||
spec.add_development_dependency 'ronn', '~> 0.7.3'
|
||||
spec.add_development_dependency 'rspec', '~> 3.0'
|
||||
|
||||
spec.files = `git ls-files -z`.split("\x0")
|
||||
spec.files += Dir.glob('lib/bundler/man/**/*') # man/ is ignored by git
|
||||
spec.test_files = spec.files.grep(%r{^spec/})
|
||||
|
||||
spec.executables = %w(bundle bundler)
|
||||
spec.require_paths = ["lib"]
|
||||
end
|
|
@ -0,0 +1,428 @@
|
|||
require 'fileutils'
|
||||
require 'pathname'
|
||||
require 'rbconfig'
|
||||
require 'bundler/gem_path_manipulation'
|
||||
require 'bundler/rubygems_ext'
|
||||
require 'bundler/rubygems_integration'
|
||||
require 'bundler/version'
|
||||
require 'bundler/constants'
|
||||
require 'bundler/current_ruby'
|
||||
|
||||
module Bundler
|
||||
preserve_gem_path
|
||||
ORIGINAL_ENV = ENV.to_hash
|
||||
|
||||
autoload :Definition, 'bundler/definition'
|
||||
autoload :Dependency, 'bundler/dependency'
|
||||
autoload :DepProxy, 'bundler/dep_proxy'
|
||||
autoload :Deprecate, 'bundler/deprecate'
|
||||
autoload :Dsl, 'bundler/dsl'
|
||||
autoload :EndpointSpecification, 'bundler/endpoint_specification'
|
||||
autoload :Environment, 'bundler/environment'
|
||||
autoload :Env, 'bundler/env'
|
||||
autoload :Fetcher, 'bundler/fetcher'
|
||||
autoload :GemHelper, 'bundler/gem_helper'
|
||||
autoload :GemHelpers, 'bundler/gem_helpers'
|
||||
autoload :GemInstaller, 'bundler/gem_installer'
|
||||
autoload :Graph, 'bundler/graph'
|
||||
autoload :Index, 'bundler/index'
|
||||
autoload :Installer, 'bundler/installer'
|
||||
autoload :Injector, 'bundler/injector'
|
||||
autoload :LazySpecification, 'bundler/lazy_specification'
|
||||
autoload :LockfileParser, 'bundler/lockfile_parser'
|
||||
autoload :MatchPlatform, 'bundler/match_platform'
|
||||
autoload :RemoteSpecification, 'bundler/remote_specification'
|
||||
autoload :Resolver, 'bundler/resolver'
|
||||
autoload :Retry, 'bundler/retry'
|
||||
autoload :RubyVersion, 'bundler/ruby_version'
|
||||
autoload :RubyDsl, 'bundler/ruby_dsl'
|
||||
autoload :Runtime, 'bundler/runtime'
|
||||
autoload :Settings, 'bundler/settings'
|
||||
autoload :SharedHelpers, 'bundler/shared_helpers'
|
||||
autoload :SpecSet, 'bundler/spec_set'
|
||||
autoload :Source, 'bundler/source'
|
||||
autoload :SourceList, 'bundler/source_list'
|
||||
autoload :Specification, 'bundler/shared_helpers'
|
||||
autoload :SystemRubyVersion, 'bundler/ruby_version'
|
||||
autoload :UI, 'bundler/ui'
|
||||
|
||||
class BundlerError < StandardError
|
||||
def self.status_code(code)
|
||||
define_method(:status_code) { code }
|
||||
end
|
||||
end
|
||||
|
||||
class GemfileNotFound < BundlerError; status_code(10) ; end
|
||||
class GemNotFound < BundlerError; status_code(7) ; end
|
||||
class GemfileError < BundlerError; status_code(4) ; end
|
||||
class InstallError < BundlerError; status_code(5) ; end
|
||||
class InstallHookError < BundlerError; status_code(8) ; end
|
||||
class PathError < BundlerError; status_code(13) ; end
|
||||
class GitError < BundlerError; status_code(11) ; end
|
||||
class DeprecatedError < BundlerError; status_code(12) ; end
|
||||
class GemspecError < BundlerError; status_code(14) ; end
|
||||
class InvalidOption < BundlerError; status_code(15) ; end
|
||||
class ProductionError < BundlerError; status_code(16) ; end
|
||||
class HTTPError < BundlerError; status_code(17) ; end
|
||||
class RubyVersionMismatch < BundlerError; status_code(18) ; end
|
||||
class SecurityError < BundlerError; status_code(19) ; end
|
||||
class LockfileError < BundlerError; status_code(20) ; end
|
||||
class CyclicDependencyError < BundlerError; status_code(21) ; end
|
||||
class GemfileLockNotFound < BundlerError; status_code(22) ; end
|
||||
|
||||
# Internal errors, should be rescued
|
||||
class VersionConflict < BundlerError
|
||||
attr_reader :conflicts
|
||||
|
||||
def initialize(conflicts, msg = nil)
|
||||
super(msg)
|
||||
@conflicts = conflicts
|
||||
end
|
||||
|
||||
status_code(6)
|
||||
end
|
||||
|
||||
class MarshalError < StandardError; end
|
||||
|
||||
class << self
|
||||
attr_writer :ui, :bundle_path
|
||||
|
||||
def configure
|
||||
@configured ||= configure_gem_home_and_path
|
||||
end
|
||||
|
||||
def ui
|
||||
@ui ||= UI::Silent.new
|
||||
end
|
||||
|
||||
# Returns absolute path of where gems are installed on the filesystem.
|
||||
def bundle_path
|
||||
@bundle_path ||= Pathname.new(settings.path).expand_path(root)
|
||||
end
|
||||
|
||||
# Returns absolute location of where binstubs are installed to.
|
||||
def bin_path
|
||||
@bin_path ||= begin
|
||||
path = settings[:bin] || "bin"
|
||||
path = Pathname.new(path).expand_path(root).expand_path
|
||||
FileUtils.mkdir_p(path)
|
||||
path
|
||||
end
|
||||
end
|
||||
|
||||
def setup(*groups)
|
||||
# Just return if all groups are already loaded
|
||||
return @setup if defined?(@setup)
|
||||
|
||||
definition.validate_ruby!
|
||||
|
||||
if groups.empty?
|
||||
# Load all groups, but only once
|
||||
@setup = load.setup
|
||||
else
|
||||
@completed_groups ||= []
|
||||
# Figure out which groups haven't been loaded yet
|
||||
unloaded = groups - @completed_groups
|
||||
# Record groups that are now loaded
|
||||
@completed_groups = groups
|
||||
unloaded.any? ? load.setup(*groups) : load
|
||||
end
|
||||
end
|
||||
|
||||
def require(*groups)
|
||||
setup(*groups).require(*groups)
|
||||
end
|
||||
|
||||
def load
|
||||
@load ||= Runtime.new(root, definition)
|
||||
end
|
||||
|
||||
def environment
|
||||
Bundler::Environment.new(root, definition)
|
||||
end
|
||||
|
||||
# Returns an instance of Bundler::Definition for given Gemfile and lockfile
|
||||
#
|
||||
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
||||
# to be updated or true if all gems should be updated
|
||||
# @return [Bundler::Definition]
|
||||
def definition(unlock = nil)
|
||||
@definition = nil if unlock
|
||||
@definition ||= begin
|
||||
configure
|
||||
upgrade_lockfile
|
||||
Definition.build(default_gemfile, default_lockfile, unlock)
|
||||
end
|
||||
end
|
||||
|
||||
def locked_gems
|
||||
return @locked_gems if defined?(@locked_gems)
|
||||
if Bundler.default_lockfile.exist?
|
||||
lock = Bundler.read_file(Bundler.default_lockfile)
|
||||
@locked_gems = LockfileParser.new(lock)
|
||||
else
|
||||
@locked_gems = nil
|
||||
end
|
||||
end
|
||||
|
||||
def ruby_scope
|
||||
"#{Bundler.rubygems.ruby_engine}/#{Bundler.rubygems.config_map[:ruby_version]}"
|
||||
end
|
||||
|
||||
def user_bundle_path
|
||||
Pathname.new(Bundler.rubygems.user_home).join(".bundler")
|
||||
end
|
||||
|
||||
def home
|
||||
bundle_path.join("bundler")
|
||||
end
|
||||
|
||||
def install_path
|
||||
home.join("gems")
|
||||
end
|
||||
|
||||
def specs_path
|
||||
bundle_path.join("specifications")
|
||||
end
|
||||
|
||||
def cache
|
||||
bundle_path.join("cache/bundler")
|
||||
end
|
||||
|
||||
def root
|
||||
@root ||= default_gemfile.dirname.expand_path
|
||||
end
|
||||
|
||||
def app_config_path
|
||||
ENV['BUNDLE_APP_CONFIG'] ?
|
||||
Pathname.new(ENV['BUNDLE_APP_CONFIG']).expand_path(root) :
|
||||
root.join('.bundle')
|
||||
end
|
||||
|
||||
def app_cache(custom_path = nil)
|
||||
path = custom_path || root
|
||||
path.join("vendor/cache")
|
||||
end
|
||||
|
||||
def tmp(name = Process.pid.to_s)
|
||||
@tmp ||= Pathname.new Dir.mktmpdir("bundler")
|
||||
@tmp.join(name)
|
||||
end
|
||||
|
||||
def settings
|
||||
@settings ||= begin
|
||||
Settings.new(app_config_path)
|
||||
rescue GemfileNotFound
|
||||
Settings.new
|
||||
end
|
||||
end
|
||||
|
||||
def with_original_env
|
||||
bundled_env = ENV.to_hash
|
||||
ENV.replace(ORIGINAL_ENV)
|
||||
yield
|
||||
ensure
|
||||
ENV.replace(bundled_env.to_hash)
|
||||
end
|
||||
|
||||
def with_clean_env
|
||||
with_original_env do
|
||||
ENV['MANPATH'] = ENV['BUNDLE_ORIG_MANPATH']
|
||||
ENV.delete_if { |k,_| k[0,7] == 'BUNDLE_' }
|
||||
if ENV.has_key? 'RUBYOPT'
|
||||
ENV['RUBYOPT'] = ENV['RUBYOPT'].sub '-rbundler/setup', ''
|
||||
ENV['RUBYOPT'] = ENV['RUBYOPT'].sub "-I#{File.expand_path('..', __FILE__)}", ''
|
||||
end
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def clean_system(*args)
|
||||
with_clean_env { Kernel.system(*args) }
|
||||
end
|
||||
|
||||
def clean_exec(*args)
|
||||
with_clean_env { Kernel.exec(*args) }
|
||||
end
|
||||
|
||||
def default_gemfile
|
||||
SharedHelpers.default_gemfile
|
||||
end
|
||||
|
||||
def default_lockfile
|
||||
SharedHelpers.default_lockfile
|
||||
end
|
||||
|
||||
def system_bindir
|
||||
# Gem.bindir doesn't always return the location that Rubygems will install
|
||||
# system binaries. If you put '-n foo' in your .gemrc, Rubygems will
|
||||
# install binstubs there instead. Unfortunately, Rubygems doesn't expose
|
||||
# that directory at all, so rather than parse .gemrc ourselves, we allow
|
||||
# the directory to be set as well, via `bundle config bindir foo`.
|
||||
Bundler.settings[:system_bindir] || Bundler.rubygems.gem_bindir
|
||||
end
|
||||
|
||||
def requires_sudo?
|
||||
return @requires_sudo if defined?(@requires_sudo_ran)
|
||||
|
||||
if settings.allow_sudo?
|
||||
sudo_present = which "sudo"
|
||||
end
|
||||
|
||||
if sudo_present
|
||||
# the bundle path and subdirectories need to be writable for Rubygems
|
||||
# to be able to unpack and install gems without exploding
|
||||
path = bundle_path
|
||||
path = path.parent until path.exist?
|
||||
|
||||
# bins are written to a different location on OS X
|
||||
bin_dir = Pathname.new(Bundler.system_bindir)
|
||||
bin_dir = bin_dir.parent until bin_dir.exist?
|
||||
|
||||
# if any directory is not writable, we need sudo
|
||||
files = [path, bin_dir] | Dir[path.join('build_info/*').to_s] | Dir[path.join('*').to_s]
|
||||
sudo_needed = files.any?{|f| !File.writable?(f) }
|
||||
end
|
||||
|
||||
@requires_sudo_ran = true
|
||||
@requires_sudo = settings.allow_sudo? && sudo_present && sudo_needed
|
||||
end
|
||||
|
||||
def mkdir_p(path)
|
||||
if requires_sudo?
|
||||
sudo "mkdir -p '#{path}'" unless File.exist?(path)
|
||||
else
|
||||
FileUtils.mkdir_p(path)
|
||||
end
|
||||
end
|
||||
|
||||
def which(executable)
|
||||
if File.file?(executable) && File.executable?(executable)
|
||||
executable
|
||||
elsif ENV['PATH']
|
||||
path = ENV['PATH'].split(File::PATH_SEPARATOR).find do |p|
|
||||
File.executable?(File.join(p, executable))
|
||||
end
|
||||
path && File.expand_path(executable, path)
|
||||
end
|
||||
end
|
||||
|
||||
def sudo(str)
|
||||
prompt = "\n\n" + <<-PROMPT.gsub(/^ {6}/, '').strip + " "
|
||||
Your user account isn't allowed to install to the system Rubygems.
|
||||
You can cancel this installation and run:
|
||||
|
||||
bundle install --path vendor/bundle
|
||||
|
||||
to install the gems into ./vendor/bundle/, or you can enter your password
|
||||
and install the bundled gems to Rubygems using sudo.
|
||||
|
||||
Password:
|
||||
PROMPT
|
||||
|
||||
`sudo -p "#{prompt}" #{str}`
|
||||
end
|
||||
|
||||
def read_file(file)
|
||||
File.open(file, "rb") { |f| f.read }
|
||||
end
|
||||
|
||||
def load_marshal(data)
|
||||
Marshal.load(data)
|
||||
rescue => e
|
||||
raise MarshalError, "#{e.class}: #{e.message}"
|
||||
end
|
||||
|
||||
def load_gemspec(file)
|
||||
@gemspec_cache ||= {}
|
||||
key = File.expand_path(file)
|
||||
spec = ( @gemspec_cache[key] ||= load_gemspec_uncached(file) )
|
||||
# Protect against caching side-effected gemspecs by returning a
|
||||
# new instance each time.
|
||||
spec.dup if spec
|
||||
end
|
||||
|
||||
def load_gemspec_uncached(file)
|
||||
path = Pathname.new(file)
|
||||
# Eval the gemspec from its parent directory, because some gemspecs
|
||||
# depend on "./" relative paths.
|
||||
SharedHelpers.chdir(path.dirname.to_s) do
|
||||
contents = path.read
|
||||
if contents[0..2] == "---" # YAML header
|
||||
eval_yaml_gemspec(path, contents)
|
||||
else
|
||||
eval_gemspec(path, contents)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def clear_gemspec_cache
|
||||
@gemspec_cache = {}
|
||||
end
|
||||
|
||||
def git_present?
|
||||
return @git_present if defined?(@git_present)
|
||||
@git_present = Bundler.which("git") || Bundler.which("git.exe")
|
||||
end
|
||||
|
||||
def ruby_version
|
||||
@ruby_version ||= SystemRubyVersion.new
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def eval_yaml_gemspec(path, contents)
|
||||
# If the YAML is invalid, Syck raises an ArgumentError, and Psych
|
||||
# raises a Psych::SyntaxError. See psyched_yaml.rb for more info.
|
||||
Gem::Specification.from_yaml(contents)
|
||||
rescue YamlSyntaxError, ArgumentError, Gem::EndOfYAMLException, Gem::Exception
|
||||
eval_gemspec(path, contents)
|
||||
end
|
||||
|
||||
def eval_gemspec(path, contents)
|
||||
eval(contents, TOPLEVEL_BINDING, path.expand_path.to_s)
|
||||
rescue ScriptError, StandardError => e
|
||||
original_line = e.backtrace.find { |line| line.include?(path.to_s) }
|
||||
msg = "There was a #{e.class} while loading #{path.basename}: \n#{e.message}"
|
||||
msg << " from\n #{original_line}" if original_line
|
||||
msg << "\n"
|
||||
|
||||
if e.is_a?(LoadError) && RUBY_VERSION >= "1.9"
|
||||
msg << "\nDoes it try to require a relative path? That's been removed in Ruby 1.9."
|
||||
end
|
||||
|
||||
raise GemspecError, msg
|
||||
end
|
||||
|
||||
def configure_gem_home_and_path
|
||||
blank_home = ENV['GEM_HOME'].nil? || ENV['GEM_HOME'].empty?
|
||||
if settings[:disable_shared_gems]
|
||||
ENV['GEM_PATH'] = ''
|
||||
elsif blank_home || Bundler.rubygems.gem_dir != bundle_path.to_s
|
||||
possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path]
|
||||
paths = possibles.flatten.compact.uniq.reject { |p| p.empty? }
|
||||
ENV["GEM_PATH"] = paths.join(File::PATH_SEPARATOR)
|
||||
end
|
||||
|
||||
configure_gem_home
|
||||
bundle_path
|
||||
end
|
||||
|
||||
def configure_gem_home
|
||||
# TODO: This mkdir_p is only needed for JRuby <= 1.5 and should go away (GH #602)
|
||||
FileUtils.mkdir_p bundle_path.to_s rescue nil
|
||||
|
||||
ENV['GEM_HOME'] = File.expand_path(bundle_path, root)
|
||||
Bundler.rubygems.clear_paths
|
||||
end
|
||||
|
||||
def upgrade_lockfile
|
||||
lockfile = default_lockfile
|
||||
if lockfile.exist? && lockfile.read(3) == "---"
|
||||
Bundler.ui.warn "Detected Gemfile.lock generated by 0.9, deleting..."
|
||||
lockfile.rmtree
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
# Capistrano task for Bundler.
|
||||
#
|
||||
# Just add "require 'bundler/capistrano'" in your Capistrano deploy.rb, and
|
||||
# Bundler will be activated after each new deployment.
|
||||
require 'bundler/deployment'
|
||||
require 'capistrano/version'
|
||||
|
||||
if defined?(Capistrano::Version) && Gem::Version.new(Capistrano::Version).release >= Gem::Version.new("3.0")
|
||||
raise "For Capistrano 3.x integration, please use http://github.com/capistrano/bundler"
|
||||
end
|
||||
|
||||
Capistrano::Configuration.instance(:must_exist).load do
|
||||
before "deploy:finalize_update", "bundle:install"
|
||||
Bundler::Deployment.define_task(self, :task, :except => { :no_release => true })
|
||||
set :rake, lambda { "#{fetch(:bundle_cmd, "bundle")} exec rake" }
|
||||
end
|
|
@ -0,0 +1,372 @@
|
|||
require 'bundler'
|
||||
require 'bundler/vendored_thor'
|
||||
|
||||
module Bundler
|
||||
class CLI < Thor
|
||||
include Thor::Actions
|
||||
|
||||
def self.start(*)
|
||||
super
|
||||
rescue Exception => e
|
||||
Bundler.ui = UI::Shell.new
|
||||
raise e
|
||||
end
|
||||
|
||||
def initialize(*)
|
||||
super
|
||||
ENV['BUNDLE_GEMFILE'] = File.expand_path(options[:gemfile]) if options[:gemfile]
|
||||
Bundler::Retry.attempts = options[:retry] || Bundler.settings[:retry] || Bundler::Retry::DEFAULT_ATTEMPTS
|
||||
Bundler.rubygems.ui = UI::RGProxy.new(Bundler.ui)
|
||||
rescue UnknownArgumentError => e
|
||||
raise InvalidOption, e.message
|
||||
ensure
|
||||
self.options ||= {}
|
||||
Bundler.ui = UI::Shell.new(options)
|
||||
Bundler.ui.level = "debug" if options["verbose"]
|
||||
end
|
||||
|
||||
check_unknown_options!(:except => [:config, :exec])
|
||||
stop_on_unknown_option! :exec
|
||||
|
||||
default_task :install
|
||||
class_option "no-color", :type => :boolean, :banner => "Disable colorization in output"
|
||||
class_option "verbose", :type => :boolean, :banner => "Enable verbose output mode", :aliases => "-V"
|
||||
class_option "retry", :type => :numeric, :aliases => "-r", :banner =>
|
||||
"Specify the number of times you wish to attempt network commands"
|
||||
|
||||
def help(cli = nil)
|
||||
case cli
|
||||
when "gemfile" then command = "gemfile.5"
|
||||
when nil then command = "bundle"
|
||||
else command = "bundle-#{cli}"
|
||||
end
|
||||
|
||||
manpages = %w(
|
||||
bundle
|
||||
bundle-config
|
||||
bundle-exec
|
||||
bundle-install
|
||||
bundle-package
|
||||
bundle-update
|
||||
bundle-platform
|
||||
gemfile.5)
|
||||
|
||||
if manpages.include?(command)
|
||||
root = File.expand_path("../man", __FILE__)
|
||||
|
||||
if Bundler.which("man") && root !~ %r{^file:/.+!/META-INF/jruby.home/.+}
|
||||
Kernel.exec "man #{root}/#{command}"
|
||||
else
|
||||
puts File.read("#{root}/#{command}.txt")
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
desc "init [OPTIONS]", "Generates a Gemfile into the current working directory"
|
||||
long_desc <<-D
|
||||
Init generates a default Gemfile in the current working directory. When adding a
|
||||
Gemfile to a gem with a gemspec, the --gemspec option will automatically add each
|
||||
dependency listed in the gemspec file to the newly created Gemfile.
|
||||
D
|
||||
method_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile"
|
||||
def init
|
||||
require 'bundler/cli/init'
|
||||
Init.new(options.dup).run
|
||||
end
|
||||
|
||||
desc "check [OPTIONS]", "Checks if the dependencies listed in Gemfile are satisfied by currently installed gems"
|
||||
long_desc <<-D
|
||||
Check searches the local machine for each of the gems requested in the Gemfile. If
|
||||
all gems are found, Bundler prints a success message and exits with a status of 0.
|
||||
If not, the first missing gem is listed and Bundler exits status 1.
|
||||
D
|
||||
method_option "gemfile", :type => :string, :banner =>
|
||||
"Use the specified gemfile instead of Gemfile"
|
||||
method_option "path", :type => :string, :banner =>
|
||||
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
|
||||
method_option "dry-run", :type => :boolean, :default => false, :banner =>
|
||||
"Lock the Gemfile"
|
||||
def check
|
||||
require 'bundler/cli/check'
|
||||
Check.new(options).run
|
||||
end
|
||||
|
||||
desc "install [OPTIONS]", "Install the current environment to the system"
|
||||
long_desc <<-D
|
||||
Install will install all of the gems in the current bundle, making them available
|
||||
for use. In a freshly checked out repository, this command will give you the same
|
||||
gem versions as the last person who updated the Gemfile and ran `bundle update`.
|
||||
|
||||
Passing [DIR] to install (e.g. vendor) will cause the unpacked gems to be installed
|
||||
into the [DIR] directory rather than into system gems.
|
||||
|
||||
If the bundle has already been installed, bundler will tell you so and then exit.
|
||||
D
|
||||
method_option "without", :type => :array, :banner =>
|
||||
"Exclude gems that are part of the specified named group."
|
||||
method_option "gemfile", :type => :string, :banner =>
|
||||
"Use the specified gemfile instead of Gemfile"
|
||||
method_option "no-prune", :type => :boolean, :banner =>
|
||||
"Don't remove stale gems from the cache."
|
||||
method_option "no-cache", :type => :boolean, :banner =>
|
||||
"Don't update the existing gem cache."
|
||||
method_option "quiet", :type => :boolean, :banner =>
|
||||
"Only output warnings and errors."
|
||||
method_option "local", :type => :boolean, :banner =>
|
||||
"Do not attempt to fetch gems remotely and use the gem cache instead"
|
||||
method_option "binstubs", :type => :string, :lazy_default => "bin", :banner =>
|
||||
"Generate bin stubs for bundled gems to ./bin"
|
||||
method_option "shebang", :type => :string, :banner =>
|
||||
"Specify a different shebang executable name than the default (usually 'ruby')"
|
||||
method_option "path", :type => :string, :banner =>
|
||||
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
|
||||
method_option "system", :type => :boolean, :banner =>
|
||||
"Install to the system location ($BUNDLE_PATH or $GEM_HOME) even if the bundle was previously installed somewhere else for this application"
|
||||
method_option "frozen", :type => :boolean, :banner =>
|
||||
"Do not allow the Gemfile.lock to be updated after this install"
|
||||
method_option "deployment", :type => :boolean, :banner =>
|
||||
"Install using defaults tuned for deployment environments"
|
||||
method_option "standalone", :type => :array, :lazy_default => [], :banner =>
|
||||
"Make a bundle that can work without the Bundler runtime"
|
||||
method_option "full-index", :type => :boolean, :banner =>
|
||||
"Use the rubygems modern index instead of the API endpoint"
|
||||
method_option "clean", :type => :boolean, :banner =>
|
||||
"Run bundle clean automatically after install"
|
||||
method_option "trust-policy", :alias => "P", :type => :string, :banner =>
|
||||
"Gem trust policy (like gem install -P). Must be one of " +
|
||||
Bundler.rubygems.security_policy_keys.join('|')
|
||||
method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
|
||||
"Specify the number of jobs to run in parallel"
|
||||
|
||||
def install
|
||||
require 'bundler/cli/install'
|
||||
Install.new(options.dup).run
|
||||
end
|
||||
|
||||
desc "update [OPTIONS]", "update the current environment"
|
||||
long_desc <<-D
|
||||
Update will install the newest versions of the gems listed in the Gemfile. Use
|
||||
update when you have changed the Gemfile, or if you want to get the newest
|
||||
possible versions of the gems in the bundle.
|
||||
D
|
||||
method_option "source", :type => :array, :banner => "Update a specific source (and all gems associated with it)"
|
||||
method_option "local", :type => :boolean, :banner =>
|
||||
"Do not attempt to fetch gems remotely and use the gem cache instead"
|
||||
method_option "quiet", :type => :boolean, :banner =>
|
||||
"Only output warnings and errors."
|
||||
method_option "full-index", :type => :boolean, :banner =>
|
||||
"Use the rubygems modern index instead of the API endpoint"
|
||||
method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
|
||||
"Specify the number of jobs to run in parallel"
|
||||
method_option "group", :aliases => "-g", :type => :array, :banner =>
|
||||
"Update a specific group"
|
||||
def update(*gems)
|
||||
require 'bundler/cli/update'
|
||||
Update.new(options, gems).run
|
||||
end
|
||||
|
||||
desc "show GEM [OPTIONS]", "Shows all gems that are part of the bundle, or the path to a given gem"
|
||||
long_desc <<-D
|
||||
Show lists the names and versions of all gems that are required by your Gemfile.
|
||||
Calling show with [GEM] will list the exact location of that gem on your machine.
|
||||
D
|
||||
method_option "paths", :type => :boolean,
|
||||
:banner => "List the paths of all gems that are required by your Gemfile."
|
||||
def show(gem_name = nil)
|
||||
require 'bundler/cli/show'
|
||||
Show.new(options, gem_name).run
|
||||
end
|
||||
map %w(list) => "show"
|
||||
|
||||
desc "binstubs GEM [OPTIONS]", "install the binstubs of the listed gem"
|
||||
long_desc <<-D
|
||||
Generate binstubs for executables in [GEM]. Binstubs are put into bin,
|
||||
or the --binstubs directory if one has been set.
|
||||
D
|
||||
method_option "path", :type => :string, :lazy_default => "bin", :banner =>
|
||||
"binstub destination directory (default bin)"
|
||||
method_option "force", :type => :boolean, :default => false, :banner =>
|
||||
"overwrite existing binstubs if they exist"
|
||||
def binstubs(*gems)
|
||||
require 'bundler/cli/binstubs'
|
||||
Binstubs.new(options, gems).run
|
||||
end
|
||||
|
||||
desc "outdated GEM [OPTIONS]", "list installed gems with newer versions available"
|
||||
long_desc <<-D
|
||||
Outdated lists the names and versions of gems that have a newer version available
|
||||
in the given source. Calling outdated with [GEM [GEM]] will only check for newer
|
||||
versions of the given gems. Prerelease gems are ignored by default. If your gems
|
||||
are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1.
|
||||
D
|
||||
method_option "pre", :type => :boolean, :banner => "Check for newer pre-release gems"
|
||||
method_option "source", :type => :array, :banner => "Check against a specific source"
|
||||
method_option "local", :type => :boolean, :banner =>
|
||||
"Do not attempt to fetch gems remotely and use the gem cache instead"
|
||||
method_option "strict", :type => :boolean, :banner =>
|
||||
"Only list newer versions allowed by your Gemfile requirements"
|
||||
def outdated(*gems)
|
||||
require 'bundler/cli/outdated'
|
||||
Outdated.new(options, gems).run
|
||||
end
|
||||
|
||||
desc "cache [OPTIONS]", "Cache all the gems to vendor/cache", :hide => true
|
||||
method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
|
||||
method_option "all", :type => :boolean, :banner => "Include all sources (including path and git)."
|
||||
def cache
|
||||
require 'bundler/cli/cache'
|
||||
Cache.new(options).run
|
||||
end
|
||||
|
||||
desc "package [OPTIONS]", "Locks and then caches all of the gems into vendor/cache"
|
||||
method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
|
||||
method_option "all", :type => :boolean, :banner => "Include all sources (including path and git)."
|
||||
method_option "quiet", :type => :boolean, :banner => "Only output warnings and errors."
|
||||
method_option "path", :type => :string, :banner =>
|
||||
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
|
||||
method_option "gemfile", :type => :string, :banner => "Use the specified gemfile instead of Gemfile"
|
||||
long_desc <<-D
|
||||
The package command will copy the .gem files for every gem in the bundle into the
|
||||
directory ./vendor/cache. If you then check that directory into your source
|
||||
control repository, others who check out your source will be able to install the
|
||||
bundle without having to download any additional gems.
|
||||
D
|
||||
def package
|
||||
require 'bundler/cli/package'
|
||||
Package.new(options).run
|
||||
end
|
||||
map %w(pack) => :package
|
||||
|
||||
desc "exec [OPTIONS]", "Run the command in context of the bundle"
|
||||
method_option :keep_file_descriptors, :type => :boolean, :default => false
|
||||
long_desc <<-D
|
||||
Exec runs a command, providing it access to the gems in the bundle. While using
|
||||
bundle exec you can require and call the bundled gems as if they were installed
|
||||
into the system wide Rubygems repository.
|
||||
D
|
||||
def exec(*args)
|
||||
require 'bundler/cli/exec'
|
||||
Exec.new(options, args).run
|
||||
end
|
||||
|
||||
desc "config NAME [VALUE]", "retrieve or set a configuration value"
|
||||
long_desc <<-D
|
||||
Retrieves or sets a configuration value. If only one parameter is provided, retrieve the value. If two parameters are provided, replace the
|
||||
existing value with the newly provided one.
|
||||
|
||||
By default, setting a configuration value sets it for all projects
|
||||
on the machine.
|
||||
|
||||
If a global setting is superceded by local configuration, this command
|
||||
will show the current value, as well as any superceded values and
|
||||
where they were specified.
|
||||
D
|
||||
def config(*args)
|
||||
require 'bundler/cli/config'
|
||||
Config.new(options, args, self).run
|
||||
end
|
||||
|
||||
desc "open GEM", "Opens the source directory of the given bundled gem"
|
||||
def open(name)
|
||||
require 'bundler/cli/open'
|
||||
Open.new(options, name).run
|
||||
end
|
||||
|
||||
CONSOLES = {
|
||||
'pry' => :Pry,
|
||||
'ripl' => :Ripl,
|
||||
'irb' => :IRB,
|
||||
}
|
||||
|
||||
desc "console [GROUP]", "Opens an IRB session with the bundle pre-loaded"
|
||||
def console(group = nil)
|
||||
require 'bundler/cli/console'
|
||||
Console.new(options, group, CONSOLES).run
|
||||
end
|
||||
|
||||
desc "version", "Prints the bundler's version information"
|
||||
def version
|
||||
Bundler.ui.info "Bundler version #{Bundler::VERSION}"
|
||||
end
|
||||
map %w(-v --version) => :version
|
||||
|
||||
desc "licenses", "Prints the license of all gems in the bundle"
|
||||
def licenses
|
||||
Bundler.load.specs.sort_by { |s| s.license.to_s }.reverse.each do |s|
|
||||
gem_name = s.name
|
||||
license = s.license || s.licenses
|
||||
|
||||
if license.empty?
|
||||
Bundler.ui.warn "#{gem_name}: Unknown"
|
||||
else
|
||||
Bundler.ui.info "#{gem_name}: #{license}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc 'viz [OPTIONS]', "Generates a visual dependency graph"
|
||||
long_desc <<-D
|
||||
Viz generates a PNG file of the current Gemfile as a dependency graph.
|
||||
Viz requires the ruby-graphviz gem (and its dependencies).
|
||||
The associated gems must also be installed via 'bundle install'.
|
||||
D
|
||||
method_option :file, :type => :string, :default => 'gem_graph', :aliases => '-f', :banner => "The name to use for the generated file. see format option"
|
||||
method_option :version, :type => :boolean, :default => false, :aliases => '-v', :banner => "Set to show each gem version."
|
||||
method_option :requirements, :type => :boolean, :default => false, :aliases => '-r', :banner => "Set to show the version of each required dependency."
|
||||
method_option :format, :type => :string, :default => "png", :aliases => '-F', :banner => "This is output format option. Supported format is png, jpg, svg, dot ..."
|
||||
def viz
|
||||
require 'bundler/cli/viz'
|
||||
Viz.new(options).run
|
||||
end
|
||||
|
||||
desc "gem GEM [OPTIONS]", "Creates a skeleton for creating a rubygem"
|
||||
method_option :bin, :type => :boolean, :default => false, :aliases => '-b', :banner => "Generate a binary for your library."
|
||||
method_option :test, :type => :string, :lazy_default => 'rspec', :aliases => '-t', :banner => "Generate a test directory for your library: 'rspec' is the default, but 'minitest' is also supported."
|
||||
method_option :edit, :type => :string, :aliases => "-e",
|
||||
:lazy_default => [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find{|e| !e.nil? && !e.empty? },
|
||||
:required => false, :banner => "/path/to/your/editor",
|
||||
:desc => "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)"
|
||||
method_option :ext, :type => :boolean, :detailt => false, :banner => "Generate the boilerplate for C extension code"
|
||||
|
||||
def gem(name)
|
||||
require 'bundler/cli/gem'
|
||||
Gem.new(options, name, self).run
|
||||
end
|
||||
|
||||
def self.source_root
|
||||
File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
||||
end
|
||||
|
||||
desc "clean [OPTIONS]", "Cleans up unused gems in your bundler directory"
|
||||
method_option "dry-run", :type => :boolean, :default => false, :banner =>
|
||||
"only print out changes, do not actually clean gems"
|
||||
method_option "force", :type => :boolean, :default => false, :banner =>
|
||||
"forces clean even if --path is not set"
|
||||
def clean
|
||||
require 'bundler/cli/clean'
|
||||
Clean.new(options.dup).run
|
||||
end
|
||||
|
||||
desc "platform [OPTIONS]", "Displays platform compatibility information"
|
||||
method_option "ruby", :type => :boolean, :default => false, :banner =>
|
||||
"only display ruby related platform information"
|
||||
def platform
|
||||
require 'bundler/cli/platform'
|
||||
Platform.new(options).run
|
||||
end
|
||||
|
||||
desc "inject GEM VERSION ...", "Add the named gem(s), with version requirements, to the resolved Gemfile"
|
||||
def inject(name, version, *gems)
|
||||
require 'bundler/cli/inject'
|
||||
Inject.new(options, name, version, gems).run
|
||||
end
|
||||
|
||||
desc "env", "Print information about the environment Bundler is running under"
|
||||
def env
|
||||
Env.new.write($stdout)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,38 @@
|
|||
require "bundler/cli/common"
|
||||
|
||||
module Bundler
|
||||
class CLI::Binstubs
|
||||
attr_reader :options, :gems
|
||||
def initialize(options, gems)
|
||||
@options = options
|
||||
@gems = gems
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.definition.validate_ruby!
|
||||
Bundler.settings[:bin] = options["path"] if options["path"]
|
||||
Bundler.settings[:bin] = nil if options["path"] && options["path"].empty?
|
||||
installer = Installer.new(Bundler.root, Bundler.definition)
|
||||
|
||||
if gems.empty?
|
||||
Bundler.ui.error "`bundle binstubs` needs at least one gem to run."
|
||||
exit 1
|
||||
end
|
||||
|
||||
gems.each do |gem_name|
|
||||
spec = installer.specs.find{|s| s.name == gem_name }
|
||||
unless spec
|
||||
raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(
|
||||
gem_name, Bundler.definition.specs)
|
||||
end
|
||||
|
||||
if spec.name == "bundler"
|
||||
Bundler.ui.warn "Sorry, Bundler can only be run via Rubygems."
|
||||
else
|
||||
installer.generate_bundler_executable_stubs(spec, :force => options[:force], :binstubs_cmd => true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
module Bundler
|
||||
class CLI::Cache
|
||||
attr_reader :options
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.definition.validate_ruby!
|
||||
Bundler.definition.resolve_with_cache!
|
||||
setup_cache_all
|
||||
Bundler.load.cache
|
||||
Bundler.settings[:no_prune] = true if options["no-prune"]
|
||||
Bundler.load.lock
|
||||
rescue GemNotFound => e
|
||||
Bundler.ui.error(e.message)
|
||||
Bundler.ui.warn "Run `bundle install` to install missing gems."
|
||||
exit 1
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setup_cache_all
|
||||
Bundler.settings[:cache_all] = options[:all] if options.key?("all")
|
||||
|
||||
if Bundler.definition.has_local_dependencies? && !Bundler.settings[:cache_all]
|
||||
Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \
|
||||
"to package them as well, please pass the --all flag. This will be the default " \
|
||||
"on Bundler 2.0."
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
module Bundler
|
||||
class CLI::Check
|
||||
attr_reader :options
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.settings[:path] = File.expand_path(options[:path]) if options[:path]
|
||||
begin
|
||||
definition = Bundler.definition
|
||||
definition.validate_ruby!
|
||||
not_installed = definition.missing_specs
|
||||
rescue GemNotFound, VersionConflict
|
||||
Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies."
|
||||
Bundler.ui.warn "Install missing gems with `bundle install`."
|
||||
exit 1
|
||||
end
|
||||
|
||||
if not_installed.any?
|
||||
Bundler.ui.error "The following gems are missing"
|
||||
not_installed.each { |s| Bundler.ui.error " * #{s.name} (#{s.version})" }
|
||||
Bundler.ui.warn "Install missing gems with `bundle install`"
|
||||
exit 1
|
||||
elsif !Bundler.default_lockfile.exist? && Bundler.settings[:frozen]
|
||||
Bundler.ui.error "This bundle has been frozen, but there is no Gemfile.lock present"
|
||||
exit 1
|
||||
else
|
||||
Bundler.load.lock unless options[:"dry-run"]
|
||||
Bundler.ui.info "The Gemfile's dependencies are satisfied"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
module Bundler
|
||||
class CLI::Clean
|
||||
attr_reader :options
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
if Bundler.settings[:path] || options[:force]
|
||||
Bundler.load.clean(options[:"dry-run"])
|
||||
else
|
||||
Bundler.ui.error "Can only use bundle clean when --path is set or --force is set"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,54 @@
|
|||
module Bundler
|
||||
module CLI::Common
|
||||
def self.without_groups_message
|
||||
groups = Bundler.settings.without
|
||||
group_list = [groups[0...-1].join(", "), groups[-1..-1]].
|
||||
reject{|s| s.to_s.empty? }.join(" and ")
|
||||
group_str = (groups.size == 1) ? "group" : "groups"
|
||||
"Gems in the #{group_str} #{group_list} were not installed."
|
||||
end
|
||||
|
||||
def self.select_spec(name, regex_match = nil)
|
||||
specs = []
|
||||
regexp = Regexp.new(name) if regex_match
|
||||
|
||||
Bundler.definition.specs.each do |spec|
|
||||
return spec if spec.name == name
|
||||
specs << spec if regexp && spec.name =~ regexp
|
||||
end
|
||||
|
||||
case specs.count
|
||||
when 0
|
||||
raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
|
||||
when 1
|
||||
specs.first
|
||||
else
|
||||
ask_for_spec_from(specs)
|
||||
end
|
||||
end
|
||||
|
||||
def self.ask_for_spec_from(specs)
|
||||
if !$stdout.tty? && ENV['BUNDLE_SPEC_RUN'].nil?
|
||||
raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
|
||||
end
|
||||
|
||||
specs.each_with_index do |spec, index|
|
||||
Bundler.ui.info "#{index.succ} : #{spec.name}", true
|
||||
end
|
||||
Bundler.ui.info '0 : - exit -', true
|
||||
|
||||
num = Bundler.ui.ask('> ').to_i
|
||||
num > 0 ? specs[num - 1] : nil
|
||||
end
|
||||
|
||||
def self.gem_not_found_message(missing_gem_name, alternatives)
|
||||
require 'bundler/similarity_detector'
|
||||
message = "Could not find gem '#{missing_gem_name}'."
|
||||
alternate_names = alternatives.map { |a| a.respond_to?(:name) ? a.name : a }
|
||||
suggestions = SimilarityDetector.new(alternate_names).similar_word_list(missing_gem_name)
|
||||
message += "\nDid you mean #{suggestions}?" if suggestions
|
||||
message
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,84 @@
|
|||
module Bundler
|
||||
class CLI::Config
|
||||
attr_reader :options, :thor
|
||||
attr_accessor :args
|
||||
|
||||
def initialize(options, args, thor)
|
||||
@options = options
|
||||
@args = args
|
||||
@thor = thor
|
||||
end
|
||||
|
||||
def run
|
||||
peek = args.shift
|
||||
|
||||
if peek && peek =~ /^\-\-/
|
||||
name, scope = args.shift, $'
|
||||
else
|
||||
name, scope = peek, "global"
|
||||
end
|
||||
|
||||
unless name
|
||||
Bundler.ui.confirm "Settings are listed in order of priority. The top value will be used.\n"
|
||||
|
||||
Bundler.settings.all.each do |setting|
|
||||
Bundler.ui.confirm "#{setting}"
|
||||
thor.with_padding do
|
||||
Bundler.settings.pretty_values_for(setting).each do |line|
|
||||
Bundler.ui.info line
|
||||
end
|
||||
end
|
||||
Bundler.ui.confirm ""
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
case scope
|
||||
when "delete"
|
||||
Bundler.settings.set_local(name, nil)
|
||||
Bundler.settings.set_global(name, nil)
|
||||
when "local", "global"
|
||||
if args.empty?
|
||||
Bundler.ui.confirm "Settings for `#{name}` in order of priority. The top value will be used"
|
||||
thor.with_padding do
|
||||
Bundler.settings.pretty_values_for(name).each { |line| Bundler.ui.info line }
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
locations = Bundler.settings.locations(name)
|
||||
|
||||
if scope == "global"
|
||||
if local = locations[:local]
|
||||
Bundler.ui.info "Your application has set #{name} to #{local.inspect}. This will override the " \
|
||||
"global value you are currently setting"
|
||||
end
|
||||
|
||||
if env = locations[:env]
|
||||
Bundler.ui.info "You have a bundler environment variable for #{name} set to #{env.inspect}. " \
|
||||
"This will take precedence over the global value you are setting"
|
||||
end
|
||||
|
||||
if global = locations[:global]
|
||||
Bundler.ui.info "You are replacing the current global value of #{name}, which is currently #{global.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
if scope == "local" && local = locations[:local]
|
||||
Bundler.ui.info "You are replacing the current local value of #{name}, which is currently #{local.inspect}"
|
||||
end
|
||||
|
||||
if name.match(/\Alocal\./)
|
||||
pathname = Pathname.new(args.join(" "))
|
||||
self.args = [pathname.expand_path.to_s] if pathname.directory?
|
||||
end
|
||||
|
||||
Bundler.settings.send("set_#{scope}", name, args.join(" "))
|
||||
else
|
||||
Bundler.ui.error "Invalid scope --#{scope} given. Please use --local or --global."
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
module Bundler
|
||||
class CLI::Console
|
||||
attr_reader :options, :group, :consoles
|
||||
def initialize(options, group, consoles)
|
||||
@options = options
|
||||
@group = group
|
||||
@consoles = consoles
|
||||
end
|
||||
|
||||
def run
|
||||
group ? Bundler.require(:default, *(group.split.map! {|g| g.to_sym })) : Bundler.require
|
||||
ARGV.clear
|
||||
|
||||
preferred = Bundler.settings[:console] || 'irb'
|
||||
|
||||
# See if console is available
|
||||
begin
|
||||
require preferred || true
|
||||
rescue LoadError
|
||||
# Is it in Gemfile?
|
||||
Bundler.ui.error "Could not load the #{preferred} console"
|
||||
Bundler.ui.info "Falling back on IRB..."
|
||||
|
||||
require 'irb'
|
||||
preferred = 'irb'
|
||||
end
|
||||
|
||||
constant = consoles[preferred]
|
||||
|
||||
console = begin
|
||||
Object.const_get(constant)
|
||||
rescue NameError => e
|
||||
Bundler.ui.error e.inspect
|
||||
Bundler.ui.error "Could not load the #{constant} console"
|
||||
return
|
||||
end
|
||||
|
||||
console.start
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
module Bundler
|
||||
class CLI::Exec
|
||||
attr_reader :options, :args
|
||||
|
||||
def initialize(options, args)
|
||||
@options = options
|
||||
@args = args
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.definition.validate_ruby!
|
||||
Bundler.load.setup_environment
|
||||
|
||||
begin
|
||||
if RUBY_VERSION >= "2.0"
|
||||
@args << { :close_others => !options.keep_file_descriptors? }
|
||||
elsif options.keep_file_descriptors?
|
||||
Bundler.ui.warn "Ruby version #{RUBY_VERSION} defaults to keeping non-standard file descriptors on Kernel#exec."
|
||||
end
|
||||
|
||||
# Run
|
||||
Kernel.exec(*args)
|
||||
rescue Errno::EACCES
|
||||
Bundler.ui.error "bundler: not executable: #{args.first}"
|
||||
exit 126
|
||||
rescue Errno::ENOENT
|
||||
Bundler.ui.error "bundler: command not found: #{args.first}"
|
||||
Bundler.ui.warn "Install missing gem executables with `bundle install`"
|
||||
exit 127
|
||||
rescue ArgumentError
|
||||
Bundler.ui.error "bundler: exec needs a command to run"
|
||||
exit 128
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,77 @@
|
|||
module Bundler
|
||||
class CLI::Gem
|
||||
attr_reader :options, :gem_name, :thor
|
||||
def initialize(options, gem_name, thor)
|
||||
@options = options
|
||||
@gem_name = gem_name
|
||||
@thor = thor
|
||||
end
|
||||
|
||||
def run
|
||||
if options[:ext] && gem_name.index('-')
|
||||
Bundler.ui.error "You have specified a gem name which does not conform to the \n" \
|
||||
"naming guidelines for C extensions. For more information, \n" \
|
||||
"see the 'Extension Naming' section at the following URL:\n" \
|
||||
"http://guides.rubygems.org/gems-with-extensions/\n"
|
||||
exit 1
|
||||
end
|
||||
|
||||
name = gem_name.chomp("/") # remove trailing slash if present
|
||||
underscored_name = name.tr('-', '_')
|
||||
namespaced_path = name.tr('-', '/')
|
||||
target = File.join(Dir.pwd, name)
|
||||
constant_name = name.split('_').map{|p| p[0..0].upcase + p[1..-1] }.join
|
||||
constant_name = constant_name.split('-').map{|q| q[0..0].upcase + q[1..-1] }.join('::') if constant_name =~ /-/
|
||||
constant_array = constant_name.split('::')
|
||||
git_user_name = `git config user.name`.chomp
|
||||
git_user_email = `git config user.email`.chomp
|
||||
opts = {
|
||||
:name => name,
|
||||
:underscored_name => underscored_name,
|
||||
:namespaced_path => namespaced_path,
|
||||
:constant_name => constant_name,
|
||||
:constant_array => constant_array,
|
||||
:author => git_user_name.empty? ? "TODO: Write your name" : git_user_name,
|
||||
:email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
|
||||
:test => options[:test],
|
||||
:ext => options[:ext]
|
||||
}
|
||||
gemspec_dest = File.join(target, "#{name}.gemspec")
|
||||
thor.template(File.join("newgem/Gemfile.tt"), File.join(target, "Gemfile"), opts)
|
||||
thor.template(File.join("newgem/Rakefile.tt"), File.join(target, "Rakefile"), opts)
|
||||
thor.template(File.join("newgem/LICENSE.txt.tt"), File.join(target, "LICENSE.txt"), opts)
|
||||
thor.template(File.join("newgem/README.md.tt"), File.join(target, "README.md"), opts)
|
||||
thor.template(File.join("newgem/gitignore.tt"), File.join(target, ".gitignore"), opts)
|
||||
thor.template(File.join("newgem/newgem.gemspec.tt"), gemspec_dest, opts)
|
||||
thor.template(File.join("newgem/lib/newgem.rb.tt"), File.join(target, "lib/#{namespaced_path}.rb"), opts)
|
||||
thor.template(File.join("newgem/lib/newgem/version.rb.tt"), File.join(target, "lib/#{namespaced_path}/version.rb"), opts)
|
||||
if options[:bin]
|
||||
thor.template(File.join("newgem/bin/newgem.tt"), File.join(target, 'bin', name), opts)
|
||||
end
|
||||
case options[:test]
|
||||
when 'rspec'
|
||||
thor.template(File.join("newgem/rspec.tt"), File.join(target, ".rspec"), opts)
|
||||
thor.template(File.join("newgem/spec/spec_helper.rb.tt"), File.join(target, "spec/spec_helper.rb"), opts)
|
||||
thor.template(File.join("newgem/spec/newgem_spec.rb.tt"), File.join(target, "spec/#{namespaced_path}_spec.rb"), opts)
|
||||
when 'minitest'
|
||||
thor.template(File.join("newgem/test/minitest_helper.rb.tt"), File.join(target, "test/minitest_helper.rb"), opts)
|
||||
thor.template(File.join("newgem/test/test_newgem.rb.tt"), File.join(target, "test/test_#{namespaced_path}.rb"), opts)
|
||||
end
|
||||
if options[:test]
|
||||
thor.template(File.join("newgem/.travis.yml.tt"), File.join(target, ".travis.yml"), opts)
|
||||
end
|
||||
if options[:ext]
|
||||
thor.template(File.join("newgem/ext/newgem/extconf.rb.tt"), File.join(target, "ext/#{name}/extconf.rb"), opts)
|
||||
thor.template(File.join("newgem/ext/newgem/newgem.h.tt"), File.join(target, "ext/#{name}/#{underscored_name}.h"), opts)
|
||||
thor.template(File.join("newgem/ext/newgem/newgem.c.tt"), File.join(target, "ext/#{name}/#{underscored_name}.c"), opts)
|
||||
end
|
||||
Bundler.ui.info "Initializing git repo in #{target}"
|
||||
Dir.chdir(target) { `git init`; `git add .` }
|
||||
|
||||
if options[:edit]
|
||||
thor.run("#{options["edit"]} \"#{gemspec_dest}\"") # Open gemspec in editor
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,33 @@
|
|||
module Bundler
|
||||
class CLI::Init
|
||||
attr_reader :options
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
if File.exist?("Gemfile")
|
||||
Bundler.ui.error "Gemfile already exists at #{Dir.pwd}/Gemfile"
|
||||
exit 1
|
||||
end
|
||||
|
||||
if options[:gemspec]
|
||||
gemspec = File.expand_path(options[:gemspec])
|
||||
unless File.exist?(gemspec)
|
||||
Bundler.ui.error "Gem specification #{gemspec} doesn't exist"
|
||||
exit 1
|
||||
end
|
||||
spec = Gem::Specification.load(gemspec)
|
||||
puts "Writing new Gemfile to #{Dir.pwd}/Gemfile"
|
||||
File.open('Gemfile', 'wb') do |file|
|
||||
file << "# Generated from #{gemspec}\n"
|
||||
file << spec.to_gemfile
|
||||
end
|
||||
else
|
||||
puts "Writing new Gemfile to #{Dir.pwd}/Gemfile"
|
||||
FileUtils.cp(File.expand_path('../../templates/Gemfile', __FILE__), 'Gemfile')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,33 @@
|
|||
module Bundler
|
||||
class CLI::Inject
|
||||
attr_reader :options, :name, :version, :gems
|
||||
def initialize(options, name, version, gems)
|
||||
@options = options
|
||||
@name = name
|
||||
@version = version
|
||||
@gems = gems
|
||||
end
|
||||
|
||||
def run
|
||||
# The required arguments allow Thor to give useful feedback when the arguments
|
||||
# are incorrect. This adds those first two arguments onto the list as a whole.
|
||||
gems.unshift(version).unshift(name)
|
||||
|
||||
# Build an array of Dependency objects out of the arguments
|
||||
deps = []
|
||||
gems.each_slice(2) do |gem_name, gem_version|
|
||||
deps << Bundler::Dependency.new(gem_name, gem_version)
|
||||
end
|
||||
|
||||
added = Injector.inject(deps)
|
||||
|
||||
if added.any?
|
||||
Bundler.ui.confirm "Added to Gemfile:"
|
||||
Bundler.ui.confirm added.map{ |g| " #{g}" }.join("\n")
|
||||
else
|
||||
Bundler.ui.confirm "All injected gems were already present in the Gemfile"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,141 @@
|
|||
module Bundler
|
||||
class CLI::Install
|
||||
attr_reader :options
|
||||
def initialize(options)
|
||||
@options = options.dup
|
||||
end
|
||||
|
||||
def run
|
||||
warn_if_root
|
||||
|
||||
if options[:without]
|
||||
options[:without] = options[:without].map{|g| g.tr(' ', ':') }
|
||||
end
|
||||
|
||||
ENV['RB_USER_INSTALL'] = '1' if Bundler::FREEBSD
|
||||
|
||||
# Just disable color in deployment mode
|
||||
Bundler.ui.shell = Thor::Shell::Basic.new if options[:deployment]
|
||||
|
||||
if (options[:path] || options[:deployment]) && options[:system]
|
||||
Bundler.ui.error "You have specified both a path to install your gems to, \n" \
|
||||
"as well as --system. Please choose."
|
||||
exit 1
|
||||
end
|
||||
|
||||
if (options["trust-policy"])
|
||||
unless (Bundler.rubygems.security_policies.keys.include?(options["trust-policy"]))
|
||||
Bundler.ui.error "Rubygems doesn't know about trust policy '#{options["trust-policy"]}'. " \
|
||||
"The known policies are: #{Bundler.rubygems.security_policies.keys.join(', ')}."
|
||||
exit 1
|
||||
end
|
||||
Bundler.settings["trust-policy"] = options["trust-policy"]
|
||||
else
|
||||
Bundler.settings["trust-policy"] = nil if Bundler.settings["trust-policy"]
|
||||
end
|
||||
|
||||
if options[:deployment] || options[:frozen]
|
||||
unless Bundler.default_lockfile.exist?
|
||||
flag = options[:deployment] ? '--deployment' : '--frozen'
|
||||
raise ProductionError, "The #{flag} flag requires a Gemfile.lock. Please make " \
|
||||
"sure you have checked your Gemfile.lock into version control " \
|
||||
"before deploying."
|
||||
end
|
||||
|
||||
if Bundler.root.join("vendor/cache").exist?
|
||||
options[:local] = true
|
||||
end
|
||||
|
||||
Bundler.settings[:frozen] = '1'
|
||||
end
|
||||
|
||||
# When install is called with --no-deployment, disable deployment mode
|
||||
if options[:deployment] == false
|
||||
Bundler.settings.delete(:frozen)
|
||||
options[:system] = true
|
||||
end
|
||||
|
||||
Bundler.settings[:path] = nil if options[:system]
|
||||
Bundler.settings[:path] = "vendor/bundle" if options[:deployment]
|
||||
Bundler.settings[:path] = options["path"] if options["path"]
|
||||
Bundler.settings[:path] ||= "bundle" if options["standalone"]
|
||||
Bundler.settings[:bin] = options["binstubs"] if options["binstubs"]
|
||||
Bundler.settings[:bin] = nil if options["binstubs"] && options["binstubs"].empty?
|
||||
Bundler.settings[:shebang] = options["shebang"] if options["shebang"]
|
||||
Bundler.settings[:jobs] = options["jobs"] if options["jobs"]
|
||||
Bundler.settings[:no_prune] = true if options["no-prune"]
|
||||
Bundler.settings[:clean] = options["clean"] if options["clean"]
|
||||
Bundler.settings.without = options[:without]
|
||||
Bundler.ui.level = "warn" if options[:quiet]
|
||||
Bundler::Fetcher.disable_endpoint = options["full-index"]
|
||||
Bundler.settings[:disable_shared_gems] = Bundler.settings[:path] ? '1' : nil
|
||||
|
||||
# rubygems plugins sometimes hook into the gem install process
|
||||
Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins)
|
||||
|
||||
definition = Bundler.definition
|
||||
definition.validate_ruby!
|
||||
Installer.install(Bundler.root, definition, options)
|
||||
Bundler.load.cache if Bundler.root.join("vendor/cache").exist? && !options["no-cache"] && !Bundler.settings[:frozen]
|
||||
|
||||
if Bundler.settings[:path]
|
||||
absolute_path = File.expand_path(Bundler.settings[:path])
|
||||
relative_path = absolute_path.sub(File.expand_path('.'), '.')
|
||||
Bundler.ui.confirm "Your bundle is complete!"
|
||||
without_groups_messages
|
||||
Bundler.ui.confirm "It was installed into #{relative_path}"
|
||||
else
|
||||
Bundler.ui.confirm "Your bundle is complete!"
|
||||
without_groups_messages
|
||||
Bundler.ui.confirm "Use `bundle show [gemname]` to see where a bundled gem is installed."
|
||||
end
|
||||
Installer.post_install_messages.to_a.each do |name, msg|
|
||||
Bundler.ui.confirm "Post-install message from #{name}:"
|
||||
Bundler.ui.info msg
|
||||
end
|
||||
Installer.ambiguous_gems.to_a.each do |name, installed_from_uri, *also_found_in_uris|
|
||||
Bundler.ui.error "Warning: the gem '#{name}' was found in multiple sources."
|
||||
Bundler.ui.error "Installed from: #{installed_from_uri}"
|
||||
Bundler.ui.error "Also found in:"
|
||||
also_found_in_uris.each { |uri| Bundler.ui.error " * #{uri}" }
|
||||
Bundler.ui.error "You should add a source requirement to restrict this gem to your preferred source."
|
||||
Bundler.ui.error "For example:"
|
||||
Bundler.ui.error " gem '#{name}', :source => '#{installed_from_uri}'"
|
||||
end
|
||||
|
||||
if Bundler.settings[:clean] && Bundler.settings[:path]
|
||||
require "bundler/cli/clean"
|
||||
Bundler::CLI::Clean.new(options).run
|
||||
end
|
||||
rescue GemNotFound, VersionConflict => e
|
||||
if options[:local] && Bundler.app_cache.exist?
|
||||
Bundler.ui.warn "Some gems seem to be missing from your vendor/cache directory."
|
||||
end
|
||||
|
||||
unless Bundler.definition.has_rubygems_remotes?
|
||||
Bundler.ui.warn <<-WARN, :wrap => true
|
||||
Your Gemfile has no gem server sources. If you need gems that are \
|
||||
not already on your machine, add a line like this to your Gemfile:
|
||||
source 'https://rubygems.org'
|
||||
WARN
|
||||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def warn_if_root
|
||||
return if Bundler::WINDOWS || !Process.uid.zero?
|
||||
Bundler.ui.warn "Don't run Bundler as root. Bundler can ask for sudo " \
|
||||
"if it is needed, and installing your bundle as root will break this " \
|
||||
"application for all non-root users on this machine.", :wrap => true
|
||||
end
|
||||
|
||||
def without_groups_messages
|
||||
if Bundler.settings.without.any?
|
||||
require "bundler/cli/common"
|
||||
Bundler.ui.confirm Bundler::CLI::Common.without_groups_message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
require 'bundler/cli/common'
|
||||
require 'shellwords'
|
||||
|
||||
module Bundler
|
||||
class CLI::Open
|
||||
attr_reader :options, :name
|
||||
def initialize(options, name)
|
||||
@options = options
|
||||
@name = name
|
||||
end
|
||||
|
||||
def run
|
||||
editor = [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find{|e| !e.nil? && !e.empty? }
|
||||
return Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") unless editor
|
||||
path = Bundler::CLI::Common.select_spec(name, :regex_match).full_gem_path
|
||||
Dir.chdir(path) do
|
||||
command = Shellwords.split(editor) + [path]
|
||||
system(*command) || Bundler.ui.info("Could not run '#{command.join(' ')}'")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,80 @@
|
|||
require 'bundler/cli/common'
|
||||
|
||||
module Bundler
|
||||
class CLI::Outdated
|
||||
attr_reader :options, :gems
|
||||
def initialize(options, gems)
|
||||
@options = options
|
||||
@gems = gems
|
||||
end
|
||||
|
||||
def run
|
||||
sources = Array(options[:source])
|
||||
|
||||
gems.each do |gem_name|
|
||||
Bundler::CLI::Common.select_spec(gem_name)
|
||||
end
|
||||
|
||||
Bundler.definition.validate_ruby!
|
||||
current_specs = Bundler.ui.silence { Bundler.load.specs }
|
||||
current_dependencies = {}
|
||||
Bundler.ui.silence { Bundler.load.dependencies.each { |dep| current_dependencies[dep.name] = dep } }
|
||||
|
||||
if gems.empty? && sources.empty?
|
||||
# We're doing a full update
|
||||
definition = Bundler.definition(true)
|
||||
else
|
||||
definition = Bundler.definition(:gems => gems, :sources => sources)
|
||||
end
|
||||
options["local"] ? definition.resolve_with_cache! : definition.resolve_remotely!
|
||||
|
||||
Bundler.ui.info ""
|
||||
|
||||
out_count = 0
|
||||
# Loop through the current specs
|
||||
gemfile_specs, dependency_specs = current_specs.partition { |spec| current_dependencies.has_key? spec.name }
|
||||
[gemfile_specs.sort_by(&:name), dependency_specs.sort_by(&:name)].flatten.each do |current_spec|
|
||||
next if !gems.empty? && !gems.include?(current_spec.name)
|
||||
|
||||
dependency = current_dependencies[current_spec.name]
|
||||
|
||||
if options["strict"]
|
||||
active_spec = definition.specs.detect { |spec| spec.name == current_spec.name }
|
||||
else
|
||||
active_spec = definition.index[current_spec.name].sort_by { |b| b.version }
|
||||
if !current_spec.version.prerelease? && !options[:pre] && active_spec.size > 1
|
||||
active_spec = active_spec.delete_if { |b| b.respond_to?(:version) && b.version.prerelease? }
|
||||
end
|
||||
active_spec = active_spec.last
|
||||
end
|
||||
next if active_spec.nil?
|
||||
|
||||
gem_outdated = Gem::Version.new(active_spec.version) > Gem::Version.new(current_spec.version)
|
||||
git_outdated = current_spec.git_version != active_spec.git_version
|
||||
if gem_outdated || git_outdated
|
||||
if out_count == 0
|
||||
if options["pre"]
|
||||
Bundler.ui.info "Outdated gems included in the bundle (including pre-releases):"
|
||||
else
|
||||
Bundler.ui.info "Outdated gems included in the bundle:"
|
||||
end
|
||||
end
|
||||
|
||||
spec_version = "#{active_spec.version}#{active_spec.git_version}"
|
||||
current_version = "#{current_spec.version}#{current_spec.git_version}"
|
||||
dependency_version = %|Gemfile specifies "#{dependency.requirement}"| if dependency && dependency.specific?
|
||||
Bundler.ui.info " * #{active_spec.name} (#{spec_version} > #{current_version}) #{dependency_version}".rstrip
|
||||
out_count += 1
|
||||
end
|
||||
Bundler.ui.debug "from #{active_spec.loaded_from}"
|
||||
end
|
||||
|
||||
if out_count.zero?
|
||||
Bundler.ui.info "Your bundle is up to date!\n"
|
||||
else
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
module Bundler
|
||||
class CLI::Package
|
||||
attr_reader :options
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.ui.level = "warn" if options[:quiet]
|
||||
Bundler.settings[:path] = File.expand_path(options[:path]) if options[:path]
|
||||
setup_cache_all
|
||||
install
|
||||
# TODO: move cache contents here now that all bundles are locked
|
||||
custom_path = Pathname.new(options[:path]) if options[:path]
|
||||
Bundler.load.cache(custom_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def install
|
||||
require 'bundler/cli/install'
|
||||
Bundler::CLI::Install.new(options.dup).run
|
||||
end
|
||||
|
||||
def setup_cache_all
|
||||
Bundler.settings[:cache_all] = options[:all] if options.key?("all")
|
||||
|
||||
if Bundler.definition.has_local_dependencies? && !Bundler.settings[:cache_all]
|
||||
Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \
|
||||
"to package them as well, please pass the --all flag. This will be the default " \
|
||||
"on Bundler 2.0."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
module Bundler
|
||||
class CLI::Platform
|
||||
attr_reader :options
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
platforms, ruby_version = Bundler.ui.silence do
|
||||
[ Bundler.definition.platforms.map {|p| "* #{p}" },
|
||||
Bundler.definition.ruby_version ]
|
||||
end
|
||||
output = []
|
||||
|
||||
if options[:ruby]
|
||||
if ruby_version
|
||||
output << ruby_version
|
||||
else
|
||||
output << "No ruby version specified"
|
||||
end
|
||||
else
|
||||
output << "Your platform is: #{RUBY_PLATFORM}"
|
||||
output << "Your app has gems that work on these platforms:\n#{platforms.join("\n")}"
|
||||
|
||||
if ruby_version
|
||||
output << "Your Gemfile specifies a Ruby version requirement:\n* #{ruby_version}"
|
||||
|
||||
begin
|
||||
Bundler.definition.validate_ruby!
|
||||
output << "Your current platform satisfies the Ruby version requirement."
|
||||
rescue RubyVersionMismatch => e
|
||||
output << e.message
|
||||
end
|
||||
else
|
||||
output << "Your Gemfile does not specify a Ruby version requirement."
|
||||
end
|
||||
end
|
||||
|
||||
Bundler.ui.info output.join("\n\n")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,48 @@
|
|||
require 'bundler/cli/common'
|
||||
|
||||
module Bundler
|
||||
class CLI::Show
|
||||
attr_reader :options, :gem_name
|
||||
def initialize(options, gem_name)
|
||||
@options = options
|
||||
@gem_name = gem_name
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.ui.silence do
|
||||
Bundler.definition.validate_ruby!
|
||||
Bundler.load.lock
|
||||
end
|
||||
|
||||
if gem_name
|
||||
if gem_name == "bundler"
|
||||
path = File.expand_path("../../../..", __FILE__)
|
||||
else
|
||||
spec = Bundler::CLI::Common.select_spec(gem_name, :regex_match)
|
||||
return unless spec
|
||||
path = spec.full_gem_path
|
||||
if !File.directory?(path)
|
||||
Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at:"
|
||||
end
|
||||
end
|
||||
return Bundler.ui.info(path)
|
||||
end
|
||||
|
||||
if options[:paths]
|
||||
Bundler.load.specs.sort_by { |s| s.name }.map do |s|
|
||||
Bundler.ui.info s.full_gem_path
|
||||
end
|
||||
else
|
||||
Bundler.ui.info "Gems included by the bundle:"
|
||||
Bundler.load.specs.sort_by { |s| s.name }.each do |s|
|
||||
desc = " * #{s.name} (#{s.version}#{s.git_version})"
|
||||
if @options[:verbose]
|
||||
Bundler.ui.info "#{desc} - #{s.summary || 'No description available.'}"
|
||||
else
|
||||
Bundler.ui.info desc
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,73 @@
|
|||
module Bundler
|
||||
class CLI::Update
|
||||
attr_reader :options, :gems
|
||||
def initialize(options, gems)
|
||||
@options = options
|
||||
@gems = gems
|
||||
end
|
||||
|
||||
def run
|
||||
|
||||
sources = Array(options[:source])
|
||||
groups = Array(options[:group]).map(&:to_sym)
|
||||
Bundler.ui.level = "warn" if options[:quiet]
|
||||
|
||||
if gems.empty? && sources.empty? && groups.empty?
|
||||
# We're doing a full update
|
||||
Bundler.definition(true)
|
||||
else
|
||||
unless Bundler.default_lockfile.exist?
|
||||
raise GemfileLockNotFound, "This Bundle hasn't been installed yet. " \
|
||||
"Run `bundle install` to update and install the bundled gems."
|
||||
end
|
||||
# cycle through the requested gems, just to make sure they exist
|
||||
names = Bundler.locked_gems.specs.map{ |s| s.name }
|
||||
gems.each do |g|
|
||||
next if names.include?(g)
|
||||
require "bundler/cli/common"
|
||||
raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(g, names)
|
||||
end
|
||||
|
||||
if groups.any?
|
||||
specs = Bundler.definition.specs_for groups
|
||||
sources.concat(specs.map(&:name))
|
||||
end
|
||||
|
||||
Bundler.definition(:gems => gems, :sources => sources)
|
||||
end
|
||||
|
||||
Bundler::Fetcher.disable_endpoint = options["full-index"]
|
||||
|
||||
opts = options.dup
|
||||
opts["update"] = true
|
||||
opts["local"] = options[:local]
|
||||
|
||||
Bundler.settings[:jobs] = opts["jobs"] if opts["jobs"]
|
||||
|
||||
# rubygems plugins sometimes hook into the gem install process
|
||||
Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins)
|
||||
|
||||
Bundler.definition.validate_ruby!
|
||||
Installer.install Bundler.root, Bundler.definition, opts
|
||||
Bundler.load.cache if Bundler.root.join("vendor/cache").exist?
|
||||
|
||||
if Bundler.settings[:clean] && Bundler.settings[:path]
|
||||
require "bundler/cli/clean"
|
||||
Bundler::CLI::Clean.new(options).run
|
||||
end
|
||||
|
||||
Bundler.ui.confirm "Your bundle is updated!"
|
||||
without_groups_messages
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def without_groups_messages
|
||||
if Bundler.settings.without.any?
|
||||
require "bundler/cli/common"
|
||||
Bundler.ui.confirm Bundler::CLI::Common.without_groups_message
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
module Bundler
|
||||
class CLI::Viz
|
||||
attr_reader :options, :gem_name
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
require 'graphviz'
|
||||
output_file = File.expand_path(options[:file])
|
||||
graph = Graph.new(Bundler.load, output_file, options[:version], options[:requirements], options[:format])
|
||||
graph.viz
|
||||
rescue LoadError => e
|
||||
Bundler.ui.error e.inspect
|
||||
Bundler.ui.warn "Make sure you have the graphviz ruby gem. You can install it with:"
|
||||
Bundler.ui.warn "`gem install ruby-graphviz`"
|
||||
rescue StandardError => e
|
||||
if e.message =~ /GraphViz not installed or dot not in PATH/
|
||||
Bundler.ui.error e.message
|
||||
Bundler.ui.warn "Please install GraphViz. On a Mac with homebrew, you can run `brew install graphviz`."
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
module Bundler
|
||||
WINDOWS = RbConfig::CONFIG["host_os"] =~ %r!(msdos|mswin|djgpp|mingw)!
|
||||
FREEBSD = RbConfig::CONFIG["host_os"] =~ /bsd/
|
||||
NULL = WINDOWS ? "NUL" : "/dev/null"
|
||||
end
|
|
@ -0,0 +1,123 @@
|
|||
module Bundler
|
||||
# Returns current version of Ruby
|
||||
#
|
||||
# @return [CurrentRuby] Current version of Ruby
|
||||
def self.current_ruby
|
||||
@current_ruby ||= CurrentRuby.new
|
||||
end
|
||||
|
||||
class CurrentRuby
|
||||
def on_18?
|
||||
RUBY_VERSION =~ /^1\.8/
|
||||
end
|
||||
|
||||
def on_19?
|
||||
RUBY_VERSION =~ /^1\.9/
|
||||
end
|
||||
|
||||
def on_20?
|
||||
RUBY_VERSION =~ /^2\.0/
|
||||
end
|
||||
|
||||
def on_21?
|
||||
RUBY_VERSION =~ /^2\.1/
|
||||
end
|
||||
|
||||
def ruby?
|
||||
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev")
|
||||
end
|
||||
|
||||
def ruby_18?
|
||||
ruby? && on_18?
|
||||
end
|
||||
|
||||
def ruby_19?
|
||||
ruby? && on_19?
|
||||
end
|
||||
|
||||
def ruby_20?
|
||||
ruby? && on_20?
|
||||
end
|
||||
|
||||
def ruby_21?
|
||||
ruby? && on_21?
|
||||
end
|
||||
|
||||
def mri?
|
||||
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby")
|
||||
end
|
||||
|
||||
def mri_18?
|
||||
mri? && on_18?
|
||||
end
|
||||
|
||||
def mri_19?
|
||||
mri? && on_19?
|
||||
end
|
||||
|
||||
def mri_20?
|
||||
mri? && on_20?
|
||||
end
|
||||
|
||||
def mri_21?
|
||||
mri? && on_21?
|
||||
end
|
||||
|
||||
def rbx?
|
||||
ruby? && defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
|
||||
end
|
||||
|
||||
def jruby?
|
||||
defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
||||
end
|
||||
|
||||
def jruby_18?
|
||||
jruby? && on_18?
|
||||
end
|
||||
|
||||
def jruby_19?
|
||||
jruby? && on_19?
|
||||
end
|
||||
|
||||
def maglev?
|
||||
defined?(RUBY_ENGINE) && RUBY_ENGINE == "maglev"
|
||||
end
|
||||
|
||||
def mswin?
|
||||
Bundler::WINDOWS
|
||||
end
|
||||
|
||||
def mingw?
|
||||
Bundler::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu != 'x64'
|
||||
end
|
||||
|
||||
def mingw_18?
|
||||
mingw? && on_18?
|
||||
end
|
||||
|
||||
def mingw_19?
|
||||
mingw? && on_19?
|
||||
end
|
||||
|
||||
def mingw_20?
|
||||
mingw? && on_20?
|
||||
end
|
||||
|
||||
def mingw_21?
|
||||
mingw? && on_21?
|
||||
end
|
||||
|
||||
def x64_mingw?
|
||||
Bundler::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu == 'x64'
|
||||
end
|
||||
|
||||
def x64_mingw_20?
|
||||
x64_mingw? && on_20?
|
||||
end
|
||||
|
||||
def x64_mingw_21?
|
||||
x64_mingw? && on_21?
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,610 @@
|
|||
require "digest/sha1"
|
||||
require "set"
|
||||
|
||||
module Bundler
|
||||
class Definition
|
||||
include GemHelpers
|
||||
|
||||
attr_reader :dependencies, :platforms, :ruby_version, :locked_deps
|
||||
|
||||
# Given a gemfile and lockfile creates a Bundler definition
|
||||
#
|
||||
# @param gemfile [Pathname] Path to Gemfile
|
||||
# @param lockfile [Pathname,nil] Path to Gemfile.lock
|
||||
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
||||
# to be updated or true if all gems should be updated
|
||||
# @return [Bundler::Definition]
|
||||
def self.build(gemfile, lockfile, unlock)
|
||||
unlock ||= {}
|
||||
gemfile = Pathname.new(gemfile).expand_path
|
||||
|
||||
unless gemfile.file?
|
||||
raise GemfileNotFound, "#{gemfile} not found"
|
||||
end
|
||||
|
||||
Dsl.evaluate(gemfile, lockfile, unlock)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# How does the new system work?
|
||||
#
|
||||
# * Load information from Gemfile and Lockfile
|
||||
# * Invalidate stale locked specs
|
||||
# * All specs from stale source are stale
|
||||
# * All specs that are reachable only through a stale
|
||||
# dependency are stale.
|
||||
# * If all fresh dependencies are satisfied by the locked
|
||||
# specs, then we can try to resolve locally.
|
||||
#
|
||||
# @param lockfile [Pathname] Path to Gemfile.lock
|
||||
# @param dependencies [Array(Bundler::Dependency)] array of dependencies from Gemfile
|
||||
# @param sources [Bundler::SourceList]
|
||||
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
||||
# to be updated or true if all gems should be updated
|
||||
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
|
||||
def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil)
|
||||
@unlocking = unlock == true || !unlock.empty?
|
||||
|
||||
@dependencies, @sources, @unlock = dependencies, sources, unlock
|
||||
@remote = false
|
||||
@specs = nil
|
||||
@lockfile_contents = ""
|
||||
@ruby_version = ruby_version
|
||||
|
||||
if lockfile && File.exist?(lockfile)
|
||||
@lockfile_contents = Bundler.read_file(lockfile)
|
||||
locked = LockfileParser.new(@lockfile_contents)
|
||||
@platforms = locked.platforms
|
||||
|
||||
if unlock != true
|
||||
@locked_deps = locked.dependencies
|
||||
@locked_specs = SpecSet.new(locked.specs)
|
||||
@locked_sources = locked.sources
|
||||
else
|
||||
@unlock = {}
|
||||
@locked_deps = []
|
||||
@locked_specs = SpecSet.new([])
|
||||
@locked_sources = []
|
||||
end
|
||||
else
|
||||
@unlock = {}
|
||||
@platforms = []
|
||||
@locked_deps = []
|
||||
@locked_specs = SpecSet.new([])
|
||||
@locked_sources = []
|
||||
end
|
||||
|
||||
@unlock[:gems] ||= []
|
||||
@unlock[:sources] ||= []
|
||||
|
||||
current_platform = Bundler.rubygems.platforms.map { |p| generic(p) }.compact.last
|
||||
@new_platform = !@platforms.include?(current_platform)
|
||||
@platforms |= [current_platform]
|
||||
|
||||
@path_changes = converge_paths
|
||||
eager_unlock = expand_dependencies(@unlock[:gems])
|
||||
@unlock[:gems] = @locked_specs.for(eager_unlock).map { |s| s.name }
|
||||
|
||||
@source_changes = converge_sources
|
||||
@dependency_changes = converge_dependencies
|
||||
@local_changes = converge_locals
|
||||
|
||||
fixup_dependency_types!
|
||||
end
|
||||
|
||||
def fixup_dependency_types!
|
||||
# XXX This is a temporary workaround for a bug when using rubygems 1.8.15
|
||||
# where Gem::Dependency#== matches Gem::Dependency#type. As the lockfile
|
||||
# doesn't carry a notion of the dependency type, if you use
|
||||
# add_development_dependency in a gemspec that's loaded with the gemspec
|
||||
# directive, the lockfile dependencies and resolved dependencies end up
|
||||
# with a mismatch on #type.
|
||||
# Test coverage to catch a regression on this is in gemspec_spec.rb
|
||||
@dependencies.each do |d|
|
||||
if ld = @locked_deps.find { |l| l.name == d.name }
|
||||
ld.instance_variable_set(:@type, d.type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_with_cache!
|
||||
raise "Specs already loaded" if @specs
|
||||
sources.cached!
|
||||
specs
|
||||
end
|
||||
|
||||
def resolve_remotely!
|
||||
raise "Specs already loaded" if @specs
|
||||
@remote = true
|
||||
sources.remote!
|
||||
specs
|
||||
end
|
||||
|
||||
# For given dependency list returns a SpecSet with Gemspec of all the required
|
||||
# dependencies.
|
||||
# 1. The method first resolves the dependencies specified in Gemfile
|
||||
# 2. After that it tries and fetches gemspec of resolved dependencies
|
||||
#
|
||||
# @return [Bundler::SpecSet]
|
||||
def specs
|
||||
@specs ||= begin
|
||||
specs = resolve.materialize(requested_dependencies)
|
||||
|
||||
unless specs["bundler"].any?
|
||||
local = Bundler.settings[:frozen] ? rubygems_index : index
|
||||
bundler = local.search(Gem::Dependency.new('bundler', VERSION)).last
|
||||
specs["bundler"] = bundler if bundler
|
||||
end
|
||||
|
||||
specs
|
||||
end
|
||||
end
|
||||
|
||||
def new_specs
|
||||
specs - @locked_specs
|
||||
end
|
||||
|
||||
def removed_specs
|
||||
@locked_specs - specs
|
||||
end
|
||||
|
||||
def new_platform?
|
||||
@new_platform
|
||||
end
|
||||
|
||||
def missing_specs
|
||||
missing = []
|
||||
resolve.materialize(requested_dependencies, missing)
|
||||
missing
|
||||
end
|
||||
|
||||
def requested_specs
|
||||
@requested_specs ||= begin
|
||||
groups = self.groups - Bundler.settings.without
|
||||
groups.map! { |g| g.to_sym }
|
||||
specs_for(groups)
|
||||
end
|
||||
end
|
||||
|
||||
def current_dependencies
|
||||
dependencies.reject { |d| !d.should_include? }
|
||||
end
|
||||
|
||||
def specs_for(groups)
|
||||
deps = dependencies.select { |d| (d.groups & groups).any? }
|
||||
deps.delete_if { |d| !d.should_include? }
|
||||
specs.for(expand_dependencies(deps))
|
||||
end
|
||||
|
||||
# Resolve all the dependencies specified in Gemfile. It ensures that
|
||||
# dependencies that have been already resolved via locked file and are fresh
|
||||
# are reused when resolving dependencies
|
||||
#
|
||||
# @return [SpecSet] resolved dependencies
|
||||
def resolve
|
||||
@resolve ||= begin
|
||||
if Bundler.settings[:frozen] || (!@unlocking && nothing_changed?)
|
||||
@locked_specs
|
||||
else
|
||||
last_resolve = converge_locked_specs
|
||||
|
||||
# Run a resolve against the locally available gems
|
||||
last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def index
|
||||
@index ||= Index.build do |idx|
|
||||
dependency_names = @dependencies.dup || []
|
||||
dependency_names.map! {|d| d.name }
|
||||
|
||||
sources.all_sources.each do |s|
|
||||
s.dependency_names = dependency_names
|
||||
idx.add_source s.specs
|
||||
dependency_names.push(*s.unmet_deps).uniq!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# used when frozen is enabled so we can find the bundler
|
||||
# spec, even if (say) a git gem is not checked out.
|
||||
def rubygems_index
|
||||
@rubygems_index ||= Index.build do |idx|
|
||||
sources.rubygems_sources.each do |rubygems|
|
||||
idx.add_source rubygems.specs
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def has_rubygems_remotes?
|
||||
sources.rubygems_sources.any? {|s| s.remotes.any? }
|
||||
end
|
||||
|
||||
def has_local_dependencies?
|
||||
!sources.path_sources.empty? || !sources.git_sources.empty?
|
||||
end
|
||||
|
||||
def spec_git_paths
|
||||
sources.git_sources.map {|s| s.path.to_s }
|
||||
end
|
||||
|
||||
def groups
|
||||
dependencies.map { |d| d.groups }.flatten.uniq
|
||||
end
|
||||
|
||||
def lock(file)
|
||||
contents = to_lock
|
||||
|
||||
# Convert to \r\n if the existing lock has them
|
||||
# i.e., Windows with `git config core.autocrlf=true`
|
||||
contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match("\r\n")
|
||||
|
||||
return if @lockfile_contents == contents
|
||||
|
||||
if Bundler.settings[:frozen]
|
||||
Bundler.ui.error "Cannot write a changed lockfile while frozen."
|
||||
return
|
||||
end
|
||||
|
||||
File.open(file, 'wb'){|f| f.puts(contents) }
|
||||
rescue Errno::EACCES
|
||||
raise Bundler::InstallError,
|
||||
"There was an error while trying to write to Gemfile.lock. It is likely that \n" \
|
||||
"you need to allow write permissions for the file at path: \n" \
|
||||
"#{File.expand_path(file)}"
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = ""
|
||||
|
||||
sources.lock_sources.each do |source|
|
||||
# Add the source header
|
||||
out << source.to_lock
|
||||
# Find all specs for this source
|
||||
resolve.
|
||||
select { |s| source.can_lock?(s) }.
|
||||
# This needs to be sorted by full name so that
|
||||
# gems with the same name, but different platform
|
||||
# are ordered consistently
|
||||
sort_by { |s| s.full_name }.
|
||||
each do |spec|
|
||||
next if spec.name == 'bundler'
|
||||
out << spec.to_lock
|
||||
end
|
||||
out << "\n"
|
||||
end
|
||||
|
||||
out << "PLATFORMS\n"
|
||||
|
||||
platforms.map { |p| p.to_s }.sort.each do |p|
|
||||
out << " #{p}\n"
|
||||
end
|
||||
|
||||
out << "\n"
|
||||
out << "DEPENDENCIES\n"
|
||||
|
||||
handled = []
|
||||
dependencies.
|
||||
sort_by { |d| d.to_s }.
|
||||
each do |dep|
|
||||
next if handled.include?(dep.name)
|
||||
out << dep.to_lock
|
||||
handled << dep.name
|
||||
end
|
||||
|
||||
out
|
||||
end
|
||||
|
||||
def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
|
||||
changes = false
|
||||
|
||||
msg = "You are trying to install in deployment mode after changing\n" \
|
||||
"your Gemfile. Run `bundle install` elsewhere and add the\n" \
|
||||
"updated Gemfile.lock to version control."
|
||||
|
||||
unless explicit_flag
|
||||
msg += "\n\nIf this is a development machine, remove the Gemfile " \
|
||||
"freeze \nby running `bundle install --no-deployment`."
|
||||
end
|
||||
|
||||
added = []
|
||||
deleted = []
|
||||
changed = []
|
||||
|
||||
gemfile_sources = sources.all_sources
|
||||
if @locked_sources != gemfile_sources
|
||||
new_sources = gemfile_sources - @locked_sources
|
||||
deleted_sources = @locked_sources - gemfile_sources
|
||||
|
||||
if new_sources.any?
|
||||
added.concat new_sources.map { |source| "* source: #{source}" }
|
||||
end
|
||||
|
||||
if deleted_sources.any?
|
||||
deleted.concat deleted_sources.map { |source| "* source: #{source}" }
|
||||
end
|
||||
|
||||
changes = true
|
||||
end
|
||||
|
||||
both_sources = Hash.new { |h,k| h[k] = ["no specified source", "no specified source"] }
|
||||
@dependencies.each { |d| both_sources[d.name][0] = d.source if d.source }
|
||||
@locked_deps.each { |d| both_sources[d.name][1] = d.source if d.source }
|
||||
both_sources.delete_if { |k,v| v[0] == v[1] }
|
||||
|
||||
if @dependencies != @locked_deps
|
||||
new_deps = @dependencies - @locked_deps
|
||||
deleted_deps = @locked_deps - @dependencies
|
||||
|
||||
if new_deps.any?
|
||||
added.concat new_deps.map { |d| "* #{pretty_dep(d)}" }
|
||||
end
|
||||
|
||||
if deleted_deps.any?
|
||||
deleted.concat deleted_deps.map { |d| "* #{pretty_dep(d)}" }
|
||||
end
|
||||
|
||||
both_sources.each do |name, sources|
|
||||
changed << "* #{name} from `#{sources[0]}` to `#{sources[1]}`"
|
||||
end
|
||||
|
||||
changes = true
|
||||
end
|
||||
|
||||
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
|
||||
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
|
||||
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
|
||||
msg << "\n"
|
||||
|
||||
raise ProductionError, msg if added.any? || deleted.any? || changed.any?
|
||||
end
|
||||
|
||||
def validate_ruby!
|
||||
return unless ruby_version
|
||||
|
||||
if diff = ruby_version.diff(Bundler.ruby_version)
|
||||
problem, expected, actual = diff
|
||||
|
||||
msg = case problem
|
||||
when :engine
|
||||
"Your Ruby engine is #{actual}, but your Gemfile specified #{expected}"
|
||||
when :version
|
||||
"Your Ruby version is #{actual}, but your Gemfile specified #{expected}"
|
||||
when :engine_version
|
||||
"Your #{Bundler.ruby_version.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}"
|
||||
when :patchlevel
|
||||
if !expected.is_a?(String)
|
||||
"The Ruby patchlevel in your Gemfile must be a string"
|
||||
else
|
||||
"Your Ruby patchlevel is #{actual}, but your Gemfile specified #{expected}"
|
||||
end
|
||||
end
|
||||
|
||||
raise RubyVersionMismatch, msg
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :sources
|
||||
|
||||
def nothing_changed?
|
||||
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes
|
||||
end
|
||||
|
||||
def pretty_dep(dep, source = false)
|
||||
msg = "#{dep.name}"
|
||||
msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
|
||||
msg << " from the `#{dep.source}` source" if source && dep.source
|
||||
msg
|
||||
end
|
||||
|
||||
# Check if the specs of the given source changed
|
||||
# according to the locked source. A block should be
|
||||
# in order to specify how the locked version of
|
||||
# the source should be found.
|
||||
def specs_changed?(source, &block)
|
||||
locked = @locked_sources.find(&block)
|
||||
|
||||
if locked
|
||||
unlocking = @locked_specs.any? do |locked_spec|
|
||||
locked_spec.source != locked
|
||||
end
|
||||
end
|
||||
|
||||
!locked || unlocking || source.specs != locked.specs
|
||||
end
|
||||
|
||||
# Get all locals and override their matching sources.
|
||||
# Return true if any of the locals changed (for example,
|
||||
# they point to a new revision) or depend on new specs.
|
||||
def converge_locals
|
||||
locals = []
|
||||
|
||||
Bundler.settings.local_overrides.map do |k,v|
|
||||
spec = @dependencies.find { |s| s.name == k }
|
||||
source = spec && spec.source
|
||||
if source && source.respond_to?(:local_override!)
|
||||
source.unlock! if @unlock[:gems].include?(spec.name)
|
||||
locals << [ source, source.local_override!(v) ]
|
||||
end
|
||||
end
|
||||
|
||||
locals.any? do |source, changed|
|
||||
changed || specs_changed?(source) { |o| source.class == o.class && source.uri == o.uri }
|
||||
end
|
||||
end
|
||||
|
||||
def converge_paths
|
||||
sources.path_sources.any? do |source|
|
||||
specs_changed?(source) do |ls|
|
||||
ls.class == source.class && ls.path == source.path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def converge_sources
|
||||
changes = false
|
||||
|
||||
# Get the Rubygems sources from the Gemfile.lock
|
||||
locked_gem_sources = @locked_sources.select { |s| s.kind_of?(Source::Rubygems) }
|
||||
# Get the Rubygems sources from the Gemfile
|
||||
actual_gem_sources = @sources.rubygems_sources
|
||||
|
||||
# If there is a Rubygems source in both
|
||||
unless locked_gem_sources.empty? && actual_gem_sources.empty?
|
||||
actual_remotes = actual_gem_sources.map(&:remotes).flatten.uniq
|
||||
locked_gem_sources.each do |locked_gem|
|
||||
# Merge the remotes from the Gemfile into the Gemfile.lock
|
||||
changes = changes | locked_gem.replace_remotes(actual_remotes)
|
||||
end
|
||||
end
|
||||
|
||||
# Replace the sources from the Gemfile with the sources from the Gemfile.lock,
|
||||
# if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
|
||||
# source in the Gemfile.lock, use the one from the Gemfile.
|
||||
sources.replace_sources!(@locked_sources)
|
||||
gemfile_sources = sources.all_sources
|
||||
changes = changes | (Set.new(gemfile_sources) != Set.new(@locked_sources))
|
||||
|
||||
gemfile_sources.each do |source|
|
||||
# If the source is unlockable and the current command allows an unlock of
|
||||
# the source (for example, you are doing a `bundle update <foo>` of a git-pinned
|
||||
# gem), unlock it. For git sources, this means to unlock the revision, which
|
||||
# will cause the `ref` used to be the most recent for the branch (or master) if
|
||||
# an explicit `ref` is not used.
|
||||
if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name)
|
||||
source.unlock!
|
||||
changes = true
|
||||
end
|
||||
end
|
||||
|
||||
changes
|
||||
end
|
||||
|
||||
def converge_dependencies
|
||||
(@dependencies + @locked_deps).each do |dep|
|
||||
if dep.source
|
||||
dep.source = sources.get(dep.source)
|
||||
end
|
||||
end
|
||||
Set.new(@dependencies) != Set.new(@locked_deps)
|
||||
end
|
||||
|
||||
# Remove elements from the locked specs that are expired. This will most
|
||||
# commonly happen if the Gemfile has changed since the lockfile was last
|
||||
# generated
|
||||
def converge_locked_specs
|
||||
deps = []
|
||||
|
||||
# Build a list of dependencies that are the same in the Gemfile
|
||||
# and Gemfile.lock. If the Gemfile modified a dependency, but
|
||||
# the gem in the Gemfile.lock still satisfies it, this is fine
|
||||
# too.
|
||||
locked_deps_hash = @locked_deps.inject({}) { |hsh, dep| hsh[dep] = dep; hsh }
|
||||
@dependencies.each do |dep|
|
||||
locked_dep = locked_deps_hash[dep]
|
||||
|
||||
if in_locked_deps?(dep, locked_dep) || satisfies_locked_spec?(dep)
|
||||
deps << dep
|
||||
elsif dep.source.is_a?(Source::Path) && dep.current_platform? && (!locked_dep || dep.source != locked_dep.source)
|
||||
@locked_specs.each do |s|
|
||||
@unlock[:gems] << s.name if s.source == dep.source
|
||||
end
|
||||
|
||||
dep.source.unlock! if dep.source.respond_to?(:unlock!)
|
||||
dep.source.specs.each { |s| @unlock[:gems] << s.name }
|
||||
end
|
||||
end
|
||||
|
||||
converged = []
|
||||
@locked_specs.each do |s|
|
||||
s.source = sources.get(s.source)
|
||||
|
||||
# Don't add a spec to the list if its source is expired. For example,
|
||||
# if you change a Git gem to Rubygems.
|
||||
next if s.source.nil? || @unlock[:sources].include?(s.name)
|
||||
# If the spec is from a path source and it doesn't exist anymore
|
||||
# then we just unlock it.
|
||||
|
||||
# Path sources have special logic
|
||||
if s.source.instance_of?(Source::Path)
|
||||
other = s.source.specs[s].first
|
||||
|
||||
# If the spec is no longer in the path source, unlock it. This
|
||||
# commonly happens if the version changed in the gemspec
|
||||
next unless other
|
||||
|
||||
deps2 = other.dependencies.select { |d| d.type != :development }
|
||||
# If the dependencies of the path source have changed, unlock it
|
||||
next unless s.dependencies.sort == deps2.sort
|
||||
end
|
||||
|
||||
converged << s
|
||||
end
|
||||
|
||||
resolve = SpecSet.new(converged)
|
||||
resolve = resolve.for(expand_dependencies(deps, true), @unlock[:gems])
|
||||
diff = @locked_specs.to_a - resolve.to_a
|
||||
|
||||
# Now, we unlock any sources that do not have anymore gems pinned to it
|
||||
sources.all_sources.each do |source|
|
||||
next unless source.respond_to?(:unlock!)
|
||||
|
||||
unless resolve.any? { |s| s.source == source }
|
||||
source.unlock! if !diff.empty? && diff.any? { |s| s.source == source }
|
||||
end
|
||||
end
|
||||
|
||||
resolve
|
||||
end
|
||||
|
||||
def in_locked_deps?(dep, d)
|
||||
d && dep.source == d.source
|
||||
end
|
||||
|
||||
def satisfies_locked_spec?(dep)
|
||||
@locked_specs.any? { |s| s.satisfies?(dep) && (!dep.source || s.source == dep.source) }
|
||||
end
|
||||
|
||||
def expanded_dependencies
|
||||
@expanded_dependencies ||= expand_dependencies(dependencies, @remote)
|
||||
end
|
||||
|
||||
def expand_dependencies(dependencies, remote = false)
|
||||
deps = []
|
||||
dependencies.each do |dep|
|
||||
dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name)
|
||||
next unless remote || dep.current_platform?
|
||||
dep.gem_platforms(@platforms).each do |p|
|
||||
deps << DepProxy.new(dep, p) if remote || p == generic(Gem::Platform.local)
|
||||
end
|
||||
end
|
||||
deps
|
||||
end
|
||||
|
||||
def requested_dependencies
|
||||
groups = self.groups - Bundler.settings.without
|
||||
groups.map! { |g| g.to_sym }
|
||||
dependencies.reject { |d| !d.should_include? || (d.groups & groups).empty? }
|
||||
end
|
||||
|
||||
def source_requirements
|
||||
# Load all specs from remote sources
|
||||
index
|
||||
|
||||
# Record the specs available in each gem's source, so that those
|
||||
# specs will be available later when the resolver knows where to
|
||||
# look for that gemspec (or its dependencies)
|
||||
source_requirements = {}
|
||||
dependencies.each do |dep|
|
||||
next unless dep.source
|
||||
source_requirements[dep.name] = dep.source.specs
|
||||
end
|
||||
source_requirements
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
module Bundler
|
||||
class DepProxy
|
||||
|
||||
attr_reader :required_by, :__platform, :dep
|
||||
|
||||
def initialize(dep, platform)
|
||||
@dep, @__platform, @required_by = dep, platform, []
|
||||
end
|
||||
|
||||
def hash
|
||||
@hash ||= dep.hash
|
||||
end
|
||||
|
||||
def ==(o)
|
||||
dep == o.dep && __platform == o.__platform
|
||||
end
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def type
|
||||
@dep.type
|
||||
end
|
||||
|
||||
def name
|
||||
@dep.name
|
||||
end
|
||||
|
||||
def requirement
|
||||
@dep.requirement
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{name} (#{requirement}) #{__platform}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def method_missing(*args)
|
||||
@dep.send(*args)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,99 @@
|
|||
require 'rubygems/dependency'
|
||||
require 'bundler/shared_helpers'
|
||||
require 'bundler/rubygems_ext'
|
||||
|
||||
module Bundler
|
||||
class Dependency < Gem::Dependency
|
||||
attr_reader :autorequire
|
||||
attr_reader :groups
|
||||
attr_reader :platforms
|
||||
|
||||
PLATFORM_MAP = {
|
||||
:ruby => Gem::Platform::RUBY,
|
||||
:ruby_18 => Gem::Platform::RUBY,
|
||||
:ruby_19 => Gem::Platform::RUBY,
|
||||
:ruby_20 => Gem::Platform::RUBY,
|
||||
:ruby_21 => Gem::Platform::RUBY,
|
||||
:mri => Gem::Platform::RUBY,
|
||||
:mri_18 => Gem::Platform::RUBY,
|
||||
:mri_19 => Gem::Platform::RUBY,
|
||||
:mri_20 => Gem::Platform::RUBY,
|
||||
:mri_21 => Gem::Platform::RUBY,
|
||||
:rbx => Gem::Platform::RUBY,
|
||||
:jruby => Gem::Platform::JAVA,
|
||||
:jruby_18 => Gem::Platform::JAVA,
|
||||
:jruby_19 => Gem::Platform::JAVA,
|
||||
:mswin => Gem::Platform::MSWIN,
|
||||
:mingw => Gem::Platform::MINGW,
|
||||
:mingw_18 => Gem::Platform::MINGW,
|
||||
:mingw_19 => Gem::Platform::MINGW,
|
||||
:mingw_20 => Gem::Platform::MINGW,
|
||||
:mingw_21 => Gem::Platform::MINGW,
|
||||
:x64_mingw => Gem::Platform::X64_MINGW,
|
||||
:x64_mingw_20 => Gem::Platform::X64_MINGW,
|
||||
:x64_mingw_21 => Gem::Platform::X64_MINGW
|
||||
}.freeze
|
||||
|
||||
def initialize(name, version, options = {}, &blk)
|
||||
type = options["type"] || :runtime
|
||||
super(name, version, type)
|
||||
|
||||
@autorequire = nil
|
||||
@groups = Array(options["group"] || :default).map { |g| g.to_sym }
|
||||
@source = options["source"]
|
||||
@platforms = Array(options["platforms"])
|
||||
@env = options["env"]
|
||||
|
||||
if options.key?('require')
|
||||
@autorequire = Array(options['require'] || [])
|
||||
end
|
||||
end
|
||||
|
||||
def gem_platforms(valid_platforms)
|
||||
return valid_platforms if @platforms.empty?
|
||||
|
||||
platforms = []
|
||||
@platforms.each do |p|
|
||||
platform = PLATFORM_MAP[p]
|
||||
next unless valid_platforms.include?(platform)
|
||||
platforms |= [platform]
|
||||
end
|
||||
platforms
|
||||
end
|
||||
|
||||
def should_include?
|
||||
current_env? && current_platform?
|
||||
end
|
||||
|
||||
def current_env?
|
||||
return true unless @env
|
||||
if @env.is_a?(Hash)
|
||||
@env.all? do |key, val|
|
||||
ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val)
|
||||
end
|
||||
else
|
||||
ENV[@env.to_s]
|
||||
end
|
||||
end
|
||||
|
||||
def current_platform?
|
||||
return true if @platforms.empty?
|
||||
@platforms.any? { |p|
|
||||
Bundler.current_ruby.send("#{p}?")
|
||||
}
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = super
|
||||
out << '!' if source
|
||||
out << "\n"
|
||||
end
|
||||
|
||||
|
||||
def specific?
|
||||
super
|
||||
rescue NoMethodError
|
||||
requirement != ">= 0"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,59 @@
|
|||
module Bundler
|
||||
class Deployment
|
||||
def self.define_task(context, task_method = :task, opts = {})
|
||||
if defined?(Capistrano) && context.is_a?(Capistrano::Configuration)
|
||||
context_name = "capistrano"
|
||||
role_default = "{:except => {:no_release => true}}"
|
||||
error_type = ::Capistrano::CommandError
|
||||
else
|
||||
context_name = "vlad"
|
||||
role_default = "[:app]"
|
||||
error_type = ::Rake::CommandFailedError
|
||||
end
|
||||
|
||||
roles = context.fetch(:bundle_roles, false)
|
||||
opts[:roles] = roles if roles
|
||||
|
||||
context.send :namespace, :bundle do
|
||||
send :desc, <<-DESC
|
||||
Install the current Bundler environment. By default, gems will be \
|
||||
installed to the shared/bundle path. Gems in the development and \
|
||||
test group will not be installed. The install command is executed \
|
||||
with the --deployment and --quiet flags. If the bundle cmd cannot \
|
||||
be found then you can override the bundle_cmd variable to specify \
|
||||
which one it should use. The base path to the app is fetched from \
|
||||
the :latest_release variable. Set it for custom deploy layouts.
|
||||
|
||||
You can override any of these defaults by setting the variables shown below.
|
||||
|
||||
N.B. bundle_roles must be defined before you require 'bundler/#{context_name}' \
|
||||
in your deploy.rb file.
|
||||
|
||||
set :bundle_gemfile, "Gemfile"
|
||||
set :bundle_dir, File.join(fetch(:shared_path), 'bundle')
|
||||
set :bundle_flags, "--deployment --quiet"
|
||||
set :bundle_without, [:development, :test]
|
||||
set :bundle_cmd, "bundle" # e.g. "/opt/ruby/bin/bundle"
|
||||
set :bundle_roles, #{role_default} # e.g. [:app, :batch]
|
||||
DESC
|
||||
send task_method, :install, opts do
|
||||
bundle_cmd = context.fetch(:bundle_cmd, "bundle")
|
||||
bundle_flags = context.fetch(:bundle_flags, "--deployment --quiet")
|
||||
bundle_dir = context.fetch(:bundle_dir, File.join(context.fetch(:shared_path), 'bundle'))
|
||||
bundle_gemfile = context.fetch(:bundle_gemfile, "Gemfile")
|
||||
bundle_without = [*context.fetch(:bundle_without, [:development, :test])].compact
|
||||
app_path = context.fetch(:latest_release)
|
||||
if app_path.to_s.empty?
|
||||
raise error_type.new("Cannot detect current release path - make sure you have deployed at least once.")
|
||||
end
|
||||
args = ["--gemfile #{File.join(app_path, bundle_gemfile)}"]
|
||||
args << "--path #{bundle_dir}" unless bundle_dir.to_s.empty?
|
||||
args << bundle_flags.to_s
|
||||
args << "--without #{bundle_without.join(" ")}" unless bundle_without.empty?
|
||||
|
||||
run "cd #{app_path} && #{bundle_cmd} install #{args.join(' ')}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
module Bundler
|
||||
|
||||
if defined? ::Deprecate
|
||||
Deprecate = ::Deprecate
|
||||
elsif defined? Gem::Deprecate
|
||||
Deprecate = Gem::Deprecate
|
||||
else
|
||||
class Deprecate; end
|
||||
end
|
||||
|
||||
unless Deprecate.respond_to?(:skip_during)
|
||||
def Deprecate.skip_during; yield; end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,294 @@
|
|||
require 'bundler/dependency'
|
||||
require 'bundler/ruby_dsl'
|
||||
|
||||
module Bundler
|
||||
class Dsl
|
||||
include RubyDsl
|
||||
|
||||
def self.evaluate(gemfile, lockfile, unlock)
|
||||
builder = new
|
||||
builder.eval_gemfile(gemfile)
|
||||
builder.to_definition(lockfile, unlock)
|
||||
end
|
||||
|
||||
VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze
|
||||
|
||||
attr_accessor :dependencies
|
||||
|
||||
def initialize
|
||||
@source = nil
|
||||
@sources = SourceList.new
|
||||
@git_sources = {}
|
||||
@dependencies = []
|
||||
@groups = []
|
||||
@platforms = []
|
||||
@env = nil
|
||||
@ruby_version = nil
|
||||
add_github_sources
|
||||
end
|
||||
|
||||
def eval_gemfile(gemfile, contents = nil)
|
||||
contents ||= Bundler.read_file(gemfile.to_s)
|
||||
instance_eval(contents, gemfile.to_s, 1)
|
||||
rescue SyntaxError => e
|
||||
syntax_msg = e.message.gsub("#{gemfile.to_s}:", 'on line ')
|
||||
raise GemfileError, "Gemfile syntax error #{syntax_msg}"
|
||||
rescue ScriptError, RegexpError, NameError, ArgumentError => e
|
||||
e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
|
||||
Bundler.ui.warn e.backtrace.join("\n ")
|
||||
raise GemfileError, "There was an error in your Gemfile," \
|
||||
" and Bundler cannot continue."
|
||||
end
|
||||
|
||||
def gemspec(opts = nil)
|
||||
path = opts && opts[:path] || '.'
|
||||
name = opts && opts[:name] || '{,*}'
|
||||
development_group = opts && opts[:development_group] || :development
|
||||
expanded_path = File.expand_path(path, Bundler.default_gemfile.dirname)
|
||||
|
||||
gemspecs = Dir[File.join(expanded_path, "#{name}.gemspec")]
|
||||
|
||||
case gemspecs.size
|
||||
when 1
|
||||
spec = Bundler.load_gemspec(gemspecs.first)
|
||||
raise InvalidOption, "There was an error loading the gemspec at #{gemspecs.first}." unless spec
|
||||
gem spec.name, :path => path
|
||||
group(development_group) do
|
||||
spec.development_dependencies.each do |dep|
|
||||
gem dep.name, *(dep.requirement.as_list + [:type => :development])
|
||||
end
|
||||
end
|
||||
when 0
|
||||
raise InvalidOption, "There are no gemspecs at #{expanded_path}."
|
||||
else
|
||||
raise InvalidOption, "There are multiple gemspecs at #{expanded_path}. Please use the :name option to specify which one."
|
||||
end
|
||||
end
|
||||
|
||||
def gem(name, *args)
|
||||
options = args.last.is_a?(Hash) ? args.pop.dup : {}
|
||||
version = args || [">= 0"]
|
||||
|
||||
normalize_options(name, version, options)
|
||||
|
||||
dep = Dependency.new(name, version, options)
|
||||
|
||||
# if there's already a dependency with this name we try to prefer one
|
||||
if current = @dependencies.find { |d| d.name == dep.name }
|
||||
if current.requirement != dep.requirement
|
||||
if current.type == :development
|
||||
@dependencies.delete current
|
||||
elsif dep.type == :development
|
||||
return
|
||||
else
|
||||
raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
|
||||
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})"
|
||||
end
|
||||
|
||||
else
|
||||
Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
|
||||
"You should probably keep only one of them.\n" \
|
||||
"While it's not a problem now, it could cause errors if you change the version of just one of them later."
|
||||
end
|
||||
|
||||
if current.source != dep.source
|
||||
if current.type == :development
|
||||
@dependencies.delete current
|
||||
elsif dep.type == :development
|
||||
return
|
||||
else
|
||||
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
|
||||
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
|
||||
"#{current.source || 'an unspecified source'} and #{dep.source}\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@dependencies << dep
|
||||
end
|
||||
|
||||
def source(source, &blk)
|
||||
source = normalize_source(source)
|
||||
if block_given?
|
||||
with_source(@sources.add_rubygems_source("remotes" => source), &blk)
|
||||
else
|
||||
@sources.add_rubygems_remote(source)
|
||||
end
|
||||
end
|
||||
|
||||
def git_source(name, &block)
|
||||
unless block_given?
|
||||
raise InvalidOption, "You need to pass a block to #git_source"
|
||||
end
|
||||
|
||||
if valid_keys.include?(name.to_s)
|
||||
raise InvalidOption, "You cannot use #{name} as a git source. It " \
|
||||
"is a reserved key. Reserved keys are: #{valid_keys.join(", ")}"
|
||||
end
|
||||
|
||||
@git_sources[name.to_s] = block
|
||||
end
|
||||
|
||||
def path(path, options = {}, &blk)
|
||||
with_source(@sources.add_path_source(normalize_hash(options).merge("path" => Pathname.new(path))), &blk)
|
||||
end
|
||||
|
||||
def git(uri, options = {}, &blk)
|
||||
unless block_given?
|
||||
msg = "You can no longer specify a git source by itself. Instead, \n" \
|
||||
"either use the :git option on a gem, or specify the gems that \n" \
|
||||
"bundler should find in the git source by passing a block to \n" \
|
||||
"the git method, like: \n\n" \
|
||||
" git 'git://github.com/rails/rails.git' do\n" \
|
||||
" gem 'rails'\n" \
|
||||
" end"
|
||||
raise DeprecatedError, msg
|
||||
end
|
||||
|
||||
with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk)
|
||||
end
|
||||
|
||||
def to_definition(lockfile, unlock)
|
||||
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version)
|
||||
end
|
||||
|
||||
def group(*args, &blk)
|
||||
@groups.concat args
|
||||
yield
|
||||
ensure
|
||||
args.each { @groups.pop }
|
||||
end
|
||||
|
||||
def platforms(*platforms)
|
||||
@platforms.concat platforms
|
||||
yield
|
||||
ensure
|
||||
platforms.each { @platforms.pop }
|
||||
end
|
||||
alias_method :platform, :platforms
|
||||
|
||||
def env(name)
|
||||
@env, old = name, @env
|
||||
yield
|
||||
ensure
|
||||
@env = old
|
||||
end
|
||||
|
||||
def method_missing(name, *args)
|
||||
location = caller[0].split(':')[0..1].join(':')
|
||||
raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile\n" \
|
||||
" from #{location}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_github_sources
|
||||
git_source(:github) do |repo_name|
|
||||
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
|
||||
"git://github.com/#{repo_name}.git"
|
||||
end
|
||||
|
||||
git_source(:gist){ |repo_name| "https://gist.github.com/#{repo_name}.git" }
|
||||
end
|
||||
|
||||
def with_source(source)
|
||||
if block_given?
|
||||
@source = source
|
||||
yield
|
||||
end
|
||||
source
|
||||
ensure
|
||||
@source = nil
|
||||
end
|
||||
|
||||
def normalize_hash(opts)
|
||||
opts.keys.each do |k|
|
||||
opts[k.to_s] = opts.delete(k) unless k.is_a?(String)
|
||||
end
|
||||
opts
|
||||
end
|
||||
|
||||
def valid_keys
|
||||
@valid_keys ||= %w(group groups git path name branch ref tag require submodules platform platforms type source)
|
||||
end
|
||||
|
||||
def normalize_options(name, version, opts)
|
||||
if name.is_a?(Symbol)
|
||||
raise GemfileError, %{You need to specify gem names as Strings. Use 'gem "#{name.to_s}"' instead.}
|
||||
end
|
||||
|
||||
normalize_hash(opts)
|
||||
|
||||
git_names = @git_sources.keys.map(&:to_s)
|
||||
|
||||
invalid_keys = opts.keys - (valid_keys + git_names)
|
||||
if invalid_keys.any?
|
||||
message = "You passed #{invalid_keys.map{|k| ':'+k }.join(", ")} "
|
||||
message << if invalid_keys.size > 1
|
||||
"as options for gem '#{name}', but they are invalid."
|
||||
else
|
||||
"as an option for gem '#{name}', but it is invalid."
|
||||
end
|
||||
|
||||
message << " Valid options are: #{valid_keys.join(", ")}"
|
||||
raise InvalidOption, message
|
||||
end
|
||||
|
||||
groups = @groups.dup
|
||||
opts["group"] = opts.delete("groups") || opts["group"]
|
||||
groups.concat Array(opts.delete("group"))
|
||||
groups = [:default] if groups.empty?
|
||||
|
||||
platforms = @platforms.dup
|
||||
opts["platforms"] = opts["platform"] || opts["platforms"]
|
||||
platforms.concat Array(opts.delete("platforms"))
|
||||
platforms.map! { |p| p.to_sym }
|
||||
platforms.each do |p|
|
||||
next if VALID_PLATFORMS.include?(p)
|
||||
raise GemfileError, "`#{p}` is not a valid platform. The available options are: #{VALID_PLATFORMS.inspect}"
|
||||
end
|
||||
|
||||
# Save sources passed in a key
|
||||
if opts.has_key?("source")
|
||||
source = normalize_source(opts["source"])
|
||||
opts["source"] = @sources.add_rubygems_source("remotes" => source)
|
||||
end
|
||||
|
||||
git_name = (git_names & opts.keys).last
|
||||
if @git_sources[git_name]
|
||||
opts["git"] = @git_sources[git_name].call(opts[git_name])
|
||||
end
|
||||
|
||||
["git", "path"].each do |type|
|
||||
if param = opts[type]
|
||||
if version.first && version.first =~ /^\s*=?\s*(\d[^\s]*)\s*$/
|
||||
options = opts.merge("name" => name, "version" => $1)
|
||||
else
|
||||
options = opts.dup
|
||||
end
|
||||
source = send(type, param, options) {}
|
||||
opts["source"] = source
|
||||
end
|
||||
end
|
||||
|
||||
opts["source"] ||= @source
|
||||
opts["env"] ||= @env
|
||||
opts["platforms"] = platforms.dup
|
||||
opts["group"] = groups
|
||||
end
|
||||
|
||||
def normalize_source(source)
|
||||
case source
|
||||
when :gemcutter, :rubygems, :rubyforge
|
||||
Bundler.ui.warn "The source :#{source} is deprecated because HTTP " \
|
||||
"requests are insecure.\nPlease change your source to 'https://" \
|
||||
"rubygems.org' if possible, or 'http://rubygems.org' if not."
|
||||
"http://rubygems.org"
|
||||
when String
|
||||
source
|
||||
else
|
||||
raise GemfileError, "Unknown source '#{source}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,76 @@
|
|||
module Bundler
|
||||
# used for Creating Specifications from the Gemcutter Endpoint
|
||||
class EndpointSpecification < Gem::Specification
|
||||
include MatchPlatform
|
||||
|
||||
attr_reader :name, :version, :platform, :dependencies
|
||||
attr_accessor :source, :source_uri
|
||||
|
||||
def initialize(name, version, platform, dependencies)
|
||||
@name = name
|
||||
@version = version
|
||||
@platform = platform
|
||||
@dependencies = dependencies
|
||||
end
|
||||
|
||||
def fetch_platform
|
||||
@platform
|
||||
end
|
||||
|
||||
# needed for standalone, load required_paths from local gemspec
|
||||
# after the gem in installed
|
||||
def require_paths
|
||||
if @remote_specification
|
||||
@remote_specification.require_paths
|
||||
elsif _local_specification
|
||||
_local_specification.require_paths
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# needed for binstubs
|
||||
def executables
|
||||
if @remote_specification
|
||||
@remote_specification.executables
|
||||
elsif _local_specification
|
||||
_local_specification.executables
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# needed for bundle clean
|
||||
def bindir
|
||||
if @remote_specification
|
||||
@remote_specification.bindir
|
||||
elsif _local_specification
|
||||
_local_specification.bindir
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# needed for post_install_messages during install
|
||||
def post_install_message
|
||||
if @remote_specification
|
||||
@remote_specification.post_install_message
|
||||
elsif _local_specification
|
||||
_local_specification.post_install_message
|
||||
end
|
||||
end
|
||||
|
||||
def _local_specification
|
||||
eval(File.read(local_specification_path)) if @loaded_from && File.exist?(local_specification_path)
|
||||
end
|
||||
|
||||
def __swap__(spec)
|
||||
@remote_specification = spec
|
||||
end
|
||||
|
||||
private
|
||||
def local_specification_path
|
||||
"#{base_dir}/specifications/#{full_name}.gemspec"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,56 @@
|
|||
module Bundler
|
||||
class Env
|
||||
|
||||
def write(io)
|
||||
io.write(report)
|
||||
end
|
||||
|
||||
def report
|
||||
out = "Bundler #{Bundler::VERSION}\n"
|
||||
|
||||
out << "Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
|
||||
out << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
|
||||
out << ") [#{RUBY_PLATFORM}]\n"
|
||||
|
||||
out << "Rubygems #{Gem::VERSION}\n"
|
||||
|
||||
out << "rvm #{ENV['rvm_version']}\n" if ENV['rvm_version']
|
||||
|
||||
out << "GEM_HOME #{ENV['GEM_HOME']}\n"
|
||||
|
||||
out << "GEM_PATH #{ENV['GEM_PATH']}\n" unless ENV['GEM_PATH'] == ENV['GEM_HOME']
|
||||
|
||||
%w(rubygems-bundler open_gem).each do |name|
|
||||
specs = Gem::Specification.find_all{|s| s.name == name }
|
||||
out << "#{name} (#{specs.map(&:version).join(',')})\n" unless specs.empty?
|
||||
end
|
||||
|
||||
out << "\nBundler settings\n" unless Bundler.settings.all.empty?
|
||||
Bundler.settings.all.each do |setting|
|
||||
out << " #{setting}\n"
|
||||
Bundler.settings.pretty_values_for(setting).each do |line|
|
||||
out << " " << line << "\n"
|
||||
end
|
||||
end
|
||||
|
||||
out << "\n\n" << "Gemfile\n"
|
||||
out << read_file("Gemfile") << "\n"
|
||||
|
||||
out << "\n\n" << "Gemfile.lock\n"
|
||||
out << read_file("Gemfile.lock") << "\n"
|
||||
|
||||
out
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def read_file(filename)
|
||||
File.read(filename).strip
|
||||
rescue Errno::ENOENT
|
||||
"<No #{filename} found>"
|
||||
rescue => e
|
||||
"#{e.class}: #{e.message}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
module Bundler
|
||||
class Environment
|
||||
attr_reader :root
|
||||
|
||||
def initialize(root, definition)
|
||||
@root = root
|
||||
@definition = definition
|
||||
|
||||
env_file = Bundler.app_config_path.join('environment.rb')
|
||||
env_file.rmtree if env_file.exist?
|
||||
end
|
||||
|
||||
def inspect
|
||||
@definition.to_lock.inspect
|
||||
end
|
||||
|
||||
def requested_specs
|
||||
@definition.requested_specs
|
||||
end
|
||||
|
||||
def specs
|
||||
@definition.specs
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@definition.dependencies
|
||||
end
|
||||
|
||||
def current_dependencies
|
||||
@definition.current_dependencies
|
||||
end
|
||||
|
||||
def lock
|
||||
@definition.lock(Bundler.default_lockfile)
|
||||
end
|
||||
|
||||
def update(*gems)
|
||||
# Nothing
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,399 @@
|
|||
require 'bundler/vendored_persistent'
|
||||
require 'securerandom'
|
||||
require 'cgi'
|
||||
|
||||
module Bundler
|
||||
|
||||
# Handles all the fetching with the rubygems server
|
||||
class Fetcher
|
||||
# This error is raised if the API returns a 413 (only printed in verbose)
|
||||
class FallbackError < HTTPError; end
|
||||
# This is the error raised if OpenSSL fails the cert verification
|
||||
class CertificateFailureError < HTTPError
|
||||
def initialize(remote_uri)
|
||||
super "Could not verify the SSL certificate for #{remote_uri}.\nThere" \
|
||||
" is a chance you are experiencing a man-in-the-middle attack, but" \
|
||||
" most likely your system doesn't have the CA certificates needed" \
|
||||
" for verification. For information about OpenSSL certificates, see" \
|
||||
" bit.ly/ruby-ssl. To connect without using SSL, edit your Gemfile" \
|
||||
" sources and change 'https' to 'http'."
|
||||
end
|
||||
end
|
||||
# This is the error raised when a source is HTTPS and OpenSSL didn't load
|
||||
class SSLError < HTTPError
|
||||
def initialize(msg = nil)
|
||||
super msg || "Could not load OpenSSL.\n" \
|
||||
"You must recompile Ruby with OpenSSL support or change the sources in your " \
|
||||
"Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL " \
|
||||
"using RVM are available at rvm.io/packages/openssl."
|
||||
end
|
||||
end
|
||||
# This error is raised if HTTP authentication is required, but not provided.
|
||||
class AuthenticationRequiredError < HTTPError
|
||||
def initialize(remote_uri)
|
||||
super "Authentication is required for #{remote_uri}.\n" \
|
||||
"Please supply credentials for this source. You can do this by running:\n" \
|
||||
" bundle config #{remote_uri} username:password"
|
||||
end
|
||||
end
|
||||
# This error is raised if HTTP authentication is provided, but incorrect.
|
||||
class BadAuthenticationError < HTTPError
|
||||
def initialize(remote_uri)
|
||||
super "Bad username or password for #{remote_uri}.\n" \
|
||||
"Please double-check your credentials and correct them."
|
||||
end
|
||||
end
|
||||
|
||||
# Exceptions classes that should bypass retry attempts. If your password didn't work the
|
||||
# first time, it's not going to the third time.
|
||||
AUTH_ERRORS = [AuthenticationRequiredError, BadAuthenticationError]
|
||||
|
||||
class << self
|
||||
attr_accessor :disable_endpoint, :api_timeout, :redirect_limit, :max_retries
|
||||
|
||||
def download_gem_from_uri(spec, uri)
|
||||
spec.fetch_platform
|
||||
|
||||
download_path = Bundler.requires_sudo? ? Bundler.tmp(spec.full_name) : Bundler.rubygems.gem_dir
|
||||
gem_path = "#{Bundler.rubygems.gem_dir}/cache/#{spec.full_name}.gem"
|
||||
|
||||
FileUtils.mkdir_p("#{download_path}/cache")
|
||||
Bundler.rubygems.download_gem(spec, uri, download_path)
|
||||
|
||||
if Bundler.requires_sudo?
|
||||
Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/cache"
|
||||
Bundler.sudo "mv #{Bundler.tmp(spec.full_name)}/cache/#{spec.full_name}.gem #{gem_path}"
|
||||
end
|
||||
|
||||
gem_path
|
||||
end
|
||||
|
||||
def user_agent
|
||||
@user_agent ||= begin
|
||||
ruby = Bundler.ruby_version
|
||||
|
||||
agent = "bundler/#{Bundler::VERSION}"
|
||||
agent += " rubygems/#{Gem::VERSION}"
|
||||
agent += " ruby/#{ruby.version}"
|
||||
agent += " (#{ruby.host})"
|
||||
agent += " command/#{ARGV.first}"
|
||||
|
||||
if ruby.engine != "ruby"
|
||||
# engine_version raises on unknown engines
|
||||
engine_version = ruby.engine_version rescue "???"
|
||||
agent += " #{ruby.engine}/#{engine_version}"
|
||||
end
|
||||
# add a random ID so we can consolidate runs server-side
|
||||
agent << " " << SecureRandom.hex(8)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def initialize(remote_uri)
|
||||
@redirect_limit = 5 # How many redirects to allow in one request
|
||||
@api_timeout = 10 # How long to wait for each API call
|
||||
@max_retries = 3 # How many retries for the API call
|
||||
|
||||
@remote_uri = Bundler::Source.mirror_for(remote_uri)
|
||||
@public_uri = @remote_uri.dup
|
||||
@public_uri.user, @public_uri.password = nil, nil # don't print these
|
||||
|
||||
Socket.do_not_reverse_lookup = true
|
||||
connection # create persistent connection
|
||||
end
|
||||
|
||||
def connection
|
||||
@connection ||= begin
|
||||
needs_ssl = @remote_uri.scheme == "https" ||
|
||||
Bundler.settings[:ssl_verify_mode] ||
|
||||
Bundler.settings[:ssl_client_cert]
|
||||
raise SSLError if needs_ssl && !defined?(OpenSSL::SSL)
|
||||
|
||||
con = Net::HTTP::Persistent.new 'bundler', :ENV
|
||||
|
||||
if @remote_uri.scheme == "https"
|
||||
con.verify_mode = (Bundler.settings[:ssl_verify_mode] ||
|
||||
OpenSSL::SSL::VERIFY_PEER)
|
||||
con.cert_store = bundler_cert_store
|
||||
end
|
||||
|
||||
if Bundler.settings[:ssl_client_cert]
|
||||
pem = File.read(Bundler.settings[:ssl_client_cert])
|
||||
con.cert = OpenSSL::X509::Certificate.new(pem)
|
||||
con.key = OpenSSL::PKey::RSA.new(pem)
|
||||
end
|
||||
|
||||
con.read_timeout = @api_timeout
|
||||
con.override_headers["User-Agent"] = self.class.user_agent
|
||||
con
|
||||
end
|
||||
end
|
||||
|
||||
def uri
|
||||
@public_uri
|
||||
end
|
||||
|
||||
# fetch a gem specification
|
||||
def fetch_spec(spec)
|
||||
spec = spec - [nil, 'ruby', '']
|
||||
spec_file_name = "#{spec.join '-'}.gemspec"
|
||||
|
||||
uri = URI.parse("#{@remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
|
||||
if uri.scheme == 'file'
|
||||
Bundler.load_marshal Gem.inflate(Gem.read_binary(uri.path))
|
||||
elsif cached_spec_path = gemspec_cached_path(spec_file_name)
|
||||
Bundler.load_gemspec(cached_spec_path)
|
||||
else
|
||||
Bundler.load_marshal Gem.inflate(fetch(uri))
|
||||
end
|
||||
rescue MarshalError
|
||||
raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
|
||||
"Your network or your gem server is probably having issues right now."
|
||||
end
|
||||
|
||||
# cached gem specification path, if one exists
|
||||
def gemspec_cached_path spec_file_name
|
||||
paths = Bundler.rubygems.spec_cache_dirs.map { |dir| File.join(dir, spec_file_name) }
|
||||
paths = paths.select {|path| File.file? path }
|
||||
paths.first
|
||||
end
|
||||
|
||||
# return the specs in the bundler format as an index
|
||||
def specs(gem_names, source)
|
||||
old = Bundler.rubygems.sources
|
||||
index = Index.new
|
||||
|
||||
if gem_names && use_api
|
||||
specs = fetch_remote_specs(gem_names)
|
||||
end
|
||||
|
||||
if specs.nil?
|
||||
# API errors mean we should treat this as a non-API source
|
||||
@use_api = false
|
||||
|
||||
specs = Bundler::Retry.new("source fetch", AUTH_ERRORS).attempts do
|
||||
fetch_all_remote_specs
|
||||
end
|
||||
end
|
||||
|
||||
specs[@remote_uri].each do |name, version, platform, dependencies|
|
||||
next if name == 'bundler'
|
||||
spec = nil
|
||||
if dependencies
|
||||
spec = EndpointSpecification.new(name, version, platform, dependencies)
|
||||
else
|
||||
spec = RemoteSpecification.new(name, version, platform, self)
|
||||
end
|
||||
spec.source = source
|
||||
spec.source_uri = @remote_uri
|
||||
index << spec
|
||||
end
|
||||
|
||||
index
|
||||
rescue CertificateFailureError => e
|
||||
Bundler.ui.info "" if gem_names && use_api # newline after dots
|
||||
raise e
|
||||
ensure
|
||||
Bundler.rubygems.sources = old
|
||||
end
|
||||
|
||||
# fetch index
|
||||
def fetch_remote_specs(gem_names, full_dependency_list = [], last_spec_list = [])
|
||||
query_list = gem_names - full_dependency_list
|
||||
|
||||
# only display the message on the first run
|
||||
if Bundler.ui.debug?
|
||||
Bundler.ui.debug "Query List: #{query_list.inspect}"
|
||||
else
|
||||
Bundler.ui.info ".", false
|
||||
end
|
||||
|
||||
return {@remote_uri => last_spec_list} if query_list.empty?
|
||||
|
||||
remote_specs = Bundler::Retry.new("dependency api", AUTH_ERRORS).attempts do
|
||||
fetch_dependency_remote_specs(query_list)
|
||||
end
|
||||
|
||||
spec_list, deps_list = remote_specs
|
||||
returned_gems = spec_list.map {|spec| spec.first }.uniq
|
||||
fetch_remote_specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
|
||||
rescue HTTPError, MarshalError, GemspecError
|
||||
Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
|
||||
Bundler.ui.debug "could not fetch from the dependency API, trying the full index"
|
||||
@use_api = false
|
||||
return nil
|
||||
end
|
||||
|
||||
def use_api
|
||||
return @use_api if defined?(@use_api)
|
||||
|
||||
if @remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint
|
||||
@use_api = false
|
||||
elsif fetch(dependency_api_uri)
|
||||
@use_api = true
|
||||
end
|
||||
rescue HTTPError
|
||||
@use_api = false
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:0x#{object_id} uri=#{uri}>"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
HTTP_ERRORS = [
|
||||
Timeout::Error, EOFError, SocketError, Errno::ENETDOWN,
|
||||
Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
|
||||
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
|
||||
Net::HTTP::Persistent::Error
|
||||
]
|
||||
|
||||
def fetch(uri, counter = 0)
|
||||
raise HTTPError, "Too many redirects" if counter >= @redirect_limit
|
||||
|
||||
response = request(uri)
|
||||
Bundler.ui.debug("HTTP #{response.code} #{response.message}")
|
||||
|
||||
case response
|
||||
when Net::HTTPRedirection
|
||||
new_uri = URI.parse(response["location"])
|
||||
if new_uri.host == uri.host
|
||||
new_uri.user = uri.user
|
||||
new_uri.password = uri.password
|
||||
end
|
||||
fetch(new_uri, counter + 1)
|
||||
when Net::HTTPSuccess
|
||||
response.body
|
||||
when Net::HTTPRequestEntityTooLarge
|
||||
raise FallbackError, response.body
|
||||
else
|
||||
raise HTTPError, "#{response.class}: #{response.body}"
|
||||
end
|
||||
end
|
||||
|
||||
def request(uri)
|
||||
Bundler.ui.debug "HTTP GET #{uri}"
|
||||
req = Net::HTTP::Get.new uri.request_uri
|
||||
if uri.user
|
||||
user = CGI.unescape(uri.user)
|
||||
password = uri.password ? CGI.unescape(uri.password) : nil
|
||||
req.basic_auth(user, password)
|
||||
end
|
||||
connection.request(uri, req)
|
||||
rescue Net::HTTPUnauthorized, Net::HTTPForbidden
|
||||
retry_with_auth { request(uri) }
|
||||
rescue OpenSSL::SSL::SSLError
|
||||
raise CertificateFailureError.new(uri)
|
||||
rescue *HTTP_ERRORS => e
|
||||
Bundler.ui.trace e
|
||||
raise HTTPError, "Network error while fetching #{uri}"
|
||||
end
|
||||
|
||||
def dependency_api_uri(gem_names = [])
|
||||
uri = fetch_uri + "api/v1/dependencies"
|
||||
uri.query = "gems=#{URI.encode(gem_names.join(","))}" if gem_names.any?
|
||||
uri
|
||||
end
|
||||
|
||||
# fetch from Gemcutter Dependency Endpoint API
|
||||
def fetch_dependency_remote_specs(gem_names)
|
||||
Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
|
||||
marshalled_deps = fetch dependency_api_uri(gem_names)
|
||||
gem_list = Bundler.load_marshal(marshalled_deps)
|
||||
deps_list = []
|
||||
|
||||
spec_list = gem_list.map do |s|
|
||||
dependencies = s[:dependencies].map do |name, requirement|
|
||||
dep = well_formed_dependency(name, requirement.split(", "))
|
||||
deps_list << dep.name
|
||||
dep
|
||||
end
|
||||
|
||||
[s[:name], Gem::Version.new(s[:number]), s[:platform], dependencies]
|
||||
end
|
||||
|
||||
[spec_list, deps_list.uniq]
|
||||
end
|
||||
|
||||
# fetch from modern index: specs.4.8.gz
|
||||
def fetch_all_remote_specs
|
||||
old_sources = Bundler.rubygems.sources
|
||||
Bundler.rubygems.sources = [@remote_uri.to_s]
|
||||
Bundler.rubygems.fetch_all_remote_specs
|
||||
rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError => e
|
||||
case e.message
|
||||
when /certificate verify failed/
|
||||
raise CertificateFailureError.new(uri)
|
||||
when /401|403/
|
||||
# Gemfury uses a 403 for unauthenticated requests instead of a 401, so retry auth
|
||||
# on both.
|
||||
retry_with_auth { fetch_all_remote_specs }
|
||||
else
|
||||
Bundler.ui.trace e
|
||||
raise HTTPError, "Could not fetch specs from #{uri}"
|
||||
end
|
||||
ensure
|
||||
Bundler.rubygems.sources = old_sources
|
||||
end
|
||||
|
||||
def well_formed_dependency(name, *requirements)
|
||||
Gem::Dependency.new(name, *requirements)
|
||||
rescue ArgumentError => e
|
||||
illformed = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey'
|
||||
raise e unless e.message.include?(illformed)
|
||||
puts # we shouldn't print the error message on the "fetching info" status line
|
||||
raise GemspecError,
|
||||
"Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid " \
|
||||
"gemspec. \nPlease ask the gem author to yank the bad version to fix " \
|
||||
"this issue. For more information, see http://bit.ly/syck-defaultkey."
|
||||
end
|
||||
|
||||
def bundler_cert_store
|
||||
store = OpenSSL::X509::Store.new
|
||||
if Bundler.settings[:ssl_ca_cert]
|
||||
if File.directory? Bundler.settings[:ssl_ca_cert]
|
||||
store.add_path Bundler.settings[:ssl_ca_cert]
|
||||
else
|
||||
store.add_file Bundler.settings[:ssl_ca_cert]
|
||||
end
|
||||
else
|
||||
store.set_default_paths
|
||||
certs = File.expand_path("../ssl_certs/*.pem", __FILE__)
|
||||
Dir.glob(certs).each { |c| store.add_file c }
|
||||
end
|
||||
store
|
||||
end
|
||||
|
||||
# Attempt to retry with HTTP authentication, if it's appropriate to do so. Yields to a block;
|
||||
# the caller should use this to re-attempt the failing request with the altered `@remote_uri`.
|
||||
def retry_with_auth
|
||||
# Authentication has already been attempted and failed.
|
||||
raise BadAuthenticationError.new(uri) if @remote_uri.user
|
||||
|
||||
auth = Bundler.settings[@remote_uri.to_s]
|
||||
|
||||
# Authentication isn't provided at all, by "bundle config" or in the URI.
|
||||
raise AuthenticationRequiredError.new(uri) if auth.nil?
|
||||
|
||||
@remote_uri.user, @remote_uri.password = *auth.split(":", 2)
|
||||
yield
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_uri
|
||||
@fetch_uri ||= begin
|
||||
if @remote_uri.host == "rubygems.org"
|
||||
uri = @remote_uri.dup
|
||||
uri.host = "bundler.rubygems.org"
|
||||
uri
|
||||
else
|
||||
@remote_uri
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
require "bundler/vendored_thor"
|
||||
|
||||
module Bundler
|
||||
def self.with_friendly_errors
|
||||
yield
|
||||
rescue Bundler::BundlerError => e
|
||||
Bundler.ui.error e.message, :wrap => true
|
||||
Bundler.ui.trace e
|
||||
exit e.status_code
|
||||
rescue Thor::AmbiguousTaskError => e
|
||||
Bundler.ui.error e.message
|
||||
exit 15
|
||||
rescue Thor::UndefinedTaskError => e
|
||||
Bundler.ui.error e.message
|
||||
exit 15
|
||||
rescue Thor::Error => e
|
||||
Bundler.ui.error e.message
|
||||
exit 1
|
||||
rescue LoadError => e
|
||||
raise e unless e.message =~ /cannot load such file -- openssl|openssl.so|libcrypto.so/
|
||||
Bundler.ui.error "\nCould not load OpenSSL."
|
||||
Bundler.ui.warn <<-WARN, :wrap => true
|
||||
You must recompile Ruby with OpenSSL support or change the sources in your \
|
||||
Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL \
|
||||
using RVM are available at http://rvm.io/packages/openssl.
|
||||
WARN
|
||||
Bundler.ui.trace e
|
||||
exit 1
|
||||
rescue Interrupt => e
|
||||
Bundler.ui.error "\nQuitting..."
|
||||
Bundler.ui.trace e
|
||||
exit 1
|
||||
rescue SystemExit => e
|
||||
exit e.status
|
||||
rescue Exception => e
|
||||
Bundler.ui.error <<-ERR, :wrap => true
|
||||
Unfortunately, a fatal error has occurred. Please see the Bundler \
|
||||
troubleshooting documentation at http://bit.ly/bundler-issues. Thanks!
|
||||
ERR
|
||||
raise e
|
||||
end
|
||||
end
|
|
@ -0,0 +1,169 @@
|
|||
require 'bundler/vendored_thor' unless defined?(Thor)
|
||||
require 'bundler'
|
||||
|
||||
module Bundler
|
||||
class GemHelper
|
||||
include Rake::DSL if defined? Rake::DSL
|
||||
|
||||
class << self
|
||||
# set when install'd.
|
||||
attr_accessor :instance
|
||||
|
||||
def install_tasks(opts = {})
|
||||
new(opts[:dir], opts[:name]).install
|
||||
end
|
||||
|
||||
def gemspec(&block)
|
||||
gemspec = instance.gemspec
|
||||
block.call(gemspec) if block
|
||||
gemspec
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :spec_path, :base, :gemspec
|
||||
|
||||
def initialize(base = nil, name = nil)
|
||||
Bundler.ui = UI::Shell.new
|
||||
@base = (base ||= SharedHelpers.pwd)
|
||||
gemspecs = name ? [File.join(base, "#{name}.gemspec")] : Dir[File.join(base, "{,*}.gemspec")]
|
||||
raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1
|
||||
@spec_path = gemspecs.first
|
||||
@gemspec = Bundler.load_gemspec(@spec_path)
|
||||
end
|
||||
|
||||
def install
|
||||
built_gem_path = nil
|
||||
|
||||
desc "Build #{name}-#{version}.gem into the pkg directory."
|
||||
task 'build' do
|
||||
built_gem_path = build_gem
|
||||
end
|
||||
|
||||
desc "Build and install #{name}-#{version}.gem into system gems."
|
||||
task 'install' => 'build' do
|
||||
install_gem(built_gem_path)
|
||||
end
|
||||
|
||||
desc "Create tag #{version_tag} and build and push #{name}-#{version}.gem to Rubygems"
|
||||
task 'release' => 'build' do
|
||||
release_gem(built_gem_path)
|
||||
end
|
||||
|
||||
GemHelper.instance = self
|
||||
end
|
||||
|
||||
def build_gem
|
||||
file_name = nil
|
||||
sh("gem build -V '#{spec_path}'") { |out, code|
|
||||
file_name = File.basename(built_gem_path)
|
||||
FileUtils.mkdir_p(File.join(base, 'pkg'))
|
||||
FileUtils.mv(built_gem_path, 'pkg')
|
||||
Bundler.ui.confirm "#{name} #{version} built to pkg/#{file_name}."
|
||||
}
|
||||
File.join(base, 'pkg', file_name)
|
||||
end
|
||||
|
||||
def install_gem(built_gem_path=nil)
|
||||
built_gem_path ||= build_gem
|
||||
out, _ = sh_with_code("gem install '#{built_gem_path}' --local")
|
||||
raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output" unless out[/Successfully installed/]
|
||||
Bundler.ui.confirm "#{name} (#{version}) installed."
|
||||
end
|
||||
|
||||
def release_gem(built_gem_path=nil)
|
||||
guard_clean
|
||||
built_gem_path ||= build_gem
|
||||
tag_version { git_push } unless already_tagged?
|
||||
rubygem_push(built_gem_path) if gem_push?
|
||||
end
|
||||
|
||||
protected
|
||||
def rubygem_push(path)
|
||||
if Pathname.new("~/.gem/credentials").expand_path.exist?
|
||||
sh("gem push '#{path}'")
|
||||
Bundler.ui.confirm "Pushed #{name} #{version} to rubygems.org."
|
||||
else
|
||||
raise "Your rubygems.org credentials aren't set. Run `gem push` to set them."
|
||||
end
|
||||
end
|
||||
|
||||
def built_gem_path
|
||||
Dir[File.join(base, "#{name}-*.gem")].sort_by{|f| File.mtime(f)}.last
|
||||
end
|
||||
|
||||
def git_push
|
||||
perform_git_push
|
||||
perform_git_push ' --tags'
|
||||
Bundler.ui.confirm "Pushed git commits and tags."
|
||||
end
|
||||
|
||||
def perform_git_push(options = '')
|
||||
cmd = "git push #{options}"
|
||||
out, code = sh_with_code(cmd)
|
||||
raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0
|
||||
end
|
||||
|
||||
def already_tagged?
|
||||
if sh('git tag').split(/\n/).include?(version_tag)
|
||||
Bundler.ui.confirm "Tag #{version_tag} has already been created."
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def guard_clean
|
||||
clean? && committed? or raise("There are files that need to be committed first.")
|
||||
end
|
||||
|
||||
def clean?
|
||||
sh_with_code("git diff --exit-code")[1] == 0
|
||||
end
|
||||
|
||||
def committed?
|
||||
sh_with_code("git diff-index --quiet --cached HEAD")[1] == 0
|
||||
end
|
||||
|
||||
def tag_version
|
||||
sh "git tag -a -m \"Version #{version}\" #{version_tag}"
|
||||
Bundler.ui.confirm "Tagged #{version_tag}."
|
||||
yield if block_given?
|
||||
rescue
|
||||
Bundler.ui.error "Untagging #{version_tag} due to error."
|
||||
sh_with_code "git tag -d #{version_tag}"
|
||||
raise
|
||||
end
|
||||
|
||||
def version
|
||||
gemspec.version
|
||||
end
|
||||
|
||||
def version_tag
|
||||
"v#{version}"
|
||||
end
|
||||
|
||||
def name
|
||||
gemspec.name
|
||||
end
|
||||
|
||||
def sh(cmd, &block)
|
||||
out, code = sh_with_code(cmd, &block)
|
||||
code == 0 ? out : raise(out.empty? ? "Running `#{cmd}' failed. Run this command directly for more detailed output." : out)
|
||||
end
|
||||
|
||||
def sh_with_code(cmd, &block)
|
||||
cmd << " 2>&1"
|
||||
outbuf = ''
|
||||
Bundler.ui.debug(cmd)
|
||||
SharedHelpers.chdir(base) {
|
||||
outbuf = `#{cmd}`
|
||||
if $? == 0
|
||||
block.call(outbuf) if block
|
||||
end
|
||||
}
|
||||
[outbuf, $?]
|
||||
end
|
||||
|
||||
def gem_push?
|
||||
! %w{n no nil false off 0}.include?(ENV['gem_push'].to_s.downcase)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
module Bundler
|
||||
module GemHelpers
|
||||
|
||||
GENERIC_CACHE = {}
|
||||
GENERICS = [
|
||||
[Gem::Platform.new('java'), Gem::Platform.new('java')],
|
||||
[Gem::Platform.new('mswin32'), Gem::Platform.new('mswin32')],
|
||||
[Gem::Platform.new('x64-mingw32'), Gem::Platform.new('x64-mingw32')],
|
||||
[Gem::Platform.new('x86_64-mingw32'), Gem::Platform.new('x64-mingw32')],
|
||||
[Gem::Platform.new('mingw32'), Gem::Platform.new('x86-mingw32')]
|
||||
]
|
||||
|
||||
def generic(p)
|
||||
return p if p == Gem::Platform::RUBY
|
||||
|
||||
GENERIC_CACHE[p] ||= begin
|
||||
_, found = GENERICS.find do |match, _generic|
|
||||
p.os == match.os && (!match.cpu || p.cpu == match.cpu)
|
||||
end
|
||||
found || Gem::Platform::RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
require 'rubygems/installer'
|
||||
|
||||
module Bundler
|
||||
class GemInstaller < Gem::Installer
|
||||
def check_executable_overwrite(filename)
|
||||
# Bundler needs to install gems regardless of binstub overwriting
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
module Bundler
|
||||
def self.preserve_gem_path
|
||||
original_gem_path = ENV["_ORIGINAL_GEM_PATH"]
|
||||
gem_path = ENV["GEM_PATH"]
|
||||
ENV["_ORIGINAL_GEM_PATH"] = gem_path if original_gem_path.nil? || original_gem_path == ""
|
||||
ENV["GEM_PATH"] = original_gem_path if gem_path.nil? || gem_path == ""
|
||||
end
|
||||
end
|
|
@ -0,0 +1,2 @@
|
|||
require 'bundler/gem_helper'
|
||||
Bundler::GemHelper.install_tasks
|
|
@ -0,0 +1,166 @@
|
|||
require 'set'
|
||||
module Bundler
|
||||
class Graph
|
||||
GRAPH_NAME = :Gemfile
|
||||
|
||||
def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png")
|
||||
@env = env
|
||||
@output_file = output_file
|
||||
@show_version = show_version
|
||||
@show_requirements = show_requirements
|
||||
@output_format = output_format
|
||||
|
||||
@groups = []
|
||||
@relations = Hash.new {|h, k| h[k] = Set.new}
|
||||
@node_options = {}
|
||||
@edge_options = {}
|
||||
|
||||
_patching_gem_dependency_class
|
||||
_populate_relations
|
||||
end
|
||||
|
||||
attr_reader :groups, :relations, :node_options, :edge_options, :output_file, :output_format
|
||||
|
||||
def viz
|
||||
GraphVizClient.new(self).run
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _populate_relations
|
||||
parent_dependencies = _groups.values.to_set.flatten
|
||||
while true
|
||||
if parent_dependencies.empty?
|
||||
break
|
||||
else
|
||||
tmp = Set.new
|
||||
parent_dependencies.each do |dependency|
|
||||
child_dependencies = dependency.to_spec.runtime_dependencies.to_set
|
||||
@relations[dependency.name] += child_dependencies.map(&:name).to_set
|
||||
tmp += child_dependencies
|
||||
|
||||
@node_options[dependency.name] = _make_label(dependency, :node)
|
||||
child_dependencies.each do |c_dependency|
|
||||
@edge_options["#{dependency.name}_#{c_dependency.name}"] = _make_label(c_dependency, :edge)
|
||||
end
|
||||
end
|
||||
parent_dependencies = tmp
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _groups
|
||||
relations = Hash.new {|h, k| h[k] = Set.new}
|
||||
@env.current_dependencies.each do |dependency|
|
||||
dependency.groups.each do |group|
|
||||
relations[group.to_s].add(dependency)
|
||||
@relations[group.to_s].add(dependency.name)
|
||||
|
||||
@node_options[group.to_s] ||= _make_label(group, :node)
|
||||
@edge_options["#{group}_#{dependency.name}"] = _make_label(dependency, :edge)
|
||||
end
|
||||
end
|
||||
@groups = relations.keys
|
||||
relations
|
||||
end
|
||||
|
||||
def _make_label(symbol_or_string_or_dependency, element_type)
|
||||
case element_type.to_sym
|
||||
when :node
|
||||
if symbol_or_string_or_dependency.is_a?(Gem::Dependency)
|
||||
label = symbol_or_string_or_dependency.name.dup
|
||||
label << "\n#{symbol_or_string_or_dependency.to_spec.version.to_s}" if @show_version
|
||||
else
|
||||
label = symbol_or_string_or_dependency.to_s
|
||||
end
|
||||
when :edge
|
||||
label = nil
|
||||
if symbol_or_string_or_dependency.respond_to?(:requirements_list) && @show_requirements
|
||||
tmp = symbol_or_string_or_dependency.requirements_list.join(", ")
|
||||
label = tmp if tmp != ">= 0"
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "2nd argument is invalid"
|
||||
end
|
||||
label.nil? ? {} : { :label => label }
|
||||
end
|
||||
|
||||
def _patching_gem_dependency_class
|
||||
# method borrow from rubygems/dependency.rb
|
||||
# redefinition of matching_specs will also redefine to_spec and to_specs
|
||||
Gem::Dependency.class_eval do
|
||||
def matching_specs platform_only = false
|
||||
matches = Bundler.load.specs.select { |spec|
|
||||
self.name == spec.name and
|
||||
requirement.satisfied_by? spec.version
|
||||
}
|
||||
|
||||
if platform_only
|
||||
matches.reject! { |spec|
|
||||
not Gem::Platform.match spec.platform
|
||||
}
|
||||
end
|
||||
|
||||
matches = matches.sort_by { |s| s.sort_obj } # HACK: shouldn't be needed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class GraphVizClient
|
||||
def initialize(graph_instance)
|
||||
@graph_name = graph_instance.class::GRAPH_NAME
|
||||
@groups = graph_instance.groups
|
||||
@relations = graph_instance.relations
|
||||
@node_options = graph_instance.node_options
|
||||
@edge_options = graph_instance.edge_options
|
||||
@output_file = graph_instance.output_file
|
||||
@output_format = graph_instance.output_format
|
||||
end
|
||||
|
||||
def g
|
||||
@g ||= ::GraphViz.digraph(@graph_name, {:concentrate => true, :normalize => true, :nodesep => 0.55}) do |g|
|
||||
g.edge[:weight] = 2
|
||||
g.edge[:fontname] = g.node[:fontname] = 'Arial, Helvetica, SansSerif'
|
||||
g.edge[:fontsize] = 12
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
@groups.each do |group|
|
||||
g.add_nodes(
|
||||
group,
|
||||
{:style => 'filled',
|
||||
:fillcolor => '#B9B9D5',
|
||||
:shape => "box3d",
|
||||
:fontsize => 16}.merge(@node_options[group])
|
||||
)
|
||||
end
|
||||
|
||||
@relations.each do |parent, children|
|
||||
children.each do |child|
|
||||
if @groups.include?(parent)
|
||||
g.add_nodes(child, {:style => 'filled', :fillcolor => '#B9B9D5'}.merge(@node_options[child]))
|
||||
g.add_edges(parent, child, {:constraint => false}.merge(@edge_options["#{parent}_#{child}"]))
|
||||
else
|
||||
g.add_nodes(child, @node_options[child])
|
||||
g.add_edges(parent, child, @edge_options["#{parent}_#{child}"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if @output_format.to_s == "debug"
|
||||
$stdout.puts g.output :none => String
|
||||
Bundler.ui.info "debugging bundle viz..."
|
||||
else
|
||||
begin
|
||||
g.output @output_format.to_sym => "#{@output_file}.#{@output_format}"
|
||||
Bundler.ui.info "#{@output_file}.#{@output_format}"
|
||||
rescue ArgumentError => e
|
||||
$stderr.puts "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb"
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,203 @@
|
|||
require "set"
|
||||
|
||||
module Bundler
|
||||
class Index
|
||||
include Enumerable
|
||||
|
||||
def self.build
|
||||
i = new
|
||||
yield i
|
||||
i
|
||||
end
|
||||
|
||||
attr_reader :specs, :all_specs, :sources
|
||||
protected :specs, :all_specs
|
||||
|
||||
def initialize
|
||||
@sources = []
|
||||
@cache = {}
|
||||
@specs = Hash.new { |h,k| h[k] = [] }
|
||||
@all_specs = Hash.new { |h,k| h[k] = [] }
|
||||
end
|
||||
|
||||
def initialize_copy(o)
|
||||
super
|
||||
@sources = @sources.dup
|
||||
@cache = {}
|
||||
@specs = Hash.new { |h,k| h[k] = [] }
|
||||
@all_specs = Hash.new { |h,k| h[k] = [] }
|
||||
|
||||
o.specs.each do |name, array|
|
||||
@specs[name] = array.dup
|
||||
end
|
||||
o.all_specs.each do |name, array|
|
||||
@all_specs[name] = array.dup
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:0x#{object_id} sources=#{sources.map{|s| s.inspect}} specs.size=#{specs.size}>"
|
||||
end
|
||||
|
||||
def empty?
|
||||
each { return false }
|
||||
true
|
||||
end
|
||||
|
||||
def search_all(name)
|
||||
all_matches = @all_specs[name] + local_search(name)
|
||||
@sources.each do |source|
|
||||
all_matches.concat(source.search_all(name))
|
||||
end
|
||||
all_matches
|
||||
end
|
||||
|
||||
# Search this index's specs, and any source indexes that this index knows
|
||||
# about, returning all of the results.
|
||||
def search(query, base = nil)
|
||||
results = local_search(query, base)
|
||||
seen = Set.new(results.map { |spec| [spec.name, spec.version, spec.platform] })
|
||||
|
||||
@sources.each do |source|
|
||||
source.search(query, base).each do |spec|
|
||||
lookup = [spec.name, spec.version, spec.platform]
|
||||
unless seen.include?(lookup)
|
||||
results << spec
|
||||
seen << lookup
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
results.sort_by {|s| [s.version, s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s] }
|
||||
end
|
||||
|
||||
def local_search(query, base = nil)
|
||||
case query
|
||||
when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query)
|
||||
when String then specs_by_name(query)
|
||||
when Gem::Dependency then search_by_dependency(query, base)
|
||||
else
|
||||
raise "You can't search for a #{query.inspect}."
|
||||
end
|
||||
end
|
||||
|
||||
def source_types
|
||||
sources.map{|s| s.class }.uniq
|
||||
end
|
||||
|
||||
alias [] search
|
||||
|
||||
def <<(spec)
|
||||
arr = specs_by_name(spec.name)
|
||||
|
||||
arr.delete_if do |s|
|
||||
same_version?(s.version, spec.version) && s.platform == spec.platform
|
||||
end
|
||||
|
||||
arr << spec
|
||||
spec
|
||||
end
|
||||
|
||||
def each(&blk)
|
||||
specs.values.each do |specs|
|
||||
specs.each(&blk)
|
||||
end
|
||||
end
|
||||
|
||||
# returns a list of the dependencies
|
||||
def unmet_dependency_names
|
||||
names = []
|
||||
each{|s| names.push *s.dependencies.map{|d| d.name } }
|
||||
names.uniq!
|
||||
names.delete_if{|n| n == "bundler" }
|
||||
names.select{|n| search(n).empty? }
|
||||
end
|
||||
|
||||
def use(other, override_dupes = false)
|
||||
return unless other
|
||||
other.each do |s|
|
||||
if (dupes = search_by_spec(s)) && dupes.any?
|
||||
@all_specs[s.name] = [s] + dupes
|
||||
next unless override_dupes
|
||||
@specs[s.name] -= dupes
|
||||
end
|
||||
@specs[s.name] << s
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def size
|
||||
@sources.inject(@specs.size) do |size, source|
|
||||
size += source.size
|
||||
end
|
||||
end
|
||||
|
||||
def ==(o)
|
||||
all? do |spec|
|
||||
other_spec = o[spec].first
|
||||
(spec.dependencies & other_spec.dependencies).empty? && spec.source == other_spec.source
|
||||
end
|
||||
end
|
||||
|
||||
def add_source(index)
|
||||
if index.is_a?(Index)
|
||||
@sources << index
|
||||
@sources.uniq! # need to use uniq! here instead of checking for the item before adding
|
||||
else
|
||||
raise ArgumentError, "Source must be an index, not #{index.class}"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def specs_by_name(name)
|
||||
@specs[name]
|
||||
end
|
||||
|
||||
def search_by_dependency(dependency, base = nil)
|
||||
@cache[base || false] ||= {}
|
||||
@cache[base || false][dependency] ||= begin
|
||||
specs = specs_by_name(dependency.name) + (base || [])
|
||||
found = specs.select do |spec|
|
||||
if base # allow all platforms when searching from a lockfile
|
||||
dependency.matches_spec?(spec)
|
||||
else
|
||||
dependency.matches_spec?(spec) && Gem::Platform.match(spec.platform)
|
||||
end
|
||||
end
|
||||
|
||||
wants_prerelease = dependency.requirement.prerelease?
|
||||
only_prerelease = specs.all? {|spec| spec.version.prerelease? }
|
||||
|
||||
unless wants_prerelease || only_prerelease
|
||||
found.reject! { |spec| spec.version.prerelease? }
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
end
|
||||
|
||||
def search_by_spec(spec)
|
||||
specs_by_name(spec.name).select do |s|
|
||||
same_version?(s.version, spec.version) && Gem::Platform.new(s.platform) == Gem::Platform.new(spec.platform)
|
||||
end
|
||||
end
|
||||
|
||||
if RUBY_VERSION < '1.9'
|
||||
def same_version?(a, b)
|
||||
regex = /^(.*?)(?:\.0)*$/
|
||||
a.to_s[regex, 1] == b.to_s[regex, 1]
|
||||
end
|
||||
else
|
||||
def same_version?(a, b)
|
||||
a == b
|
||||
end
|
||||
end
|
||||
|
||||
def spec_satisfies_dependency?(spec, dep)
|
||||
return false unless dep.name == spec.name
|
||||
dep.requirement.satisfied_by?(spec.version)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,64 @@
|
|||
module Bundler
|
||||
class Injector
|
||||
def self.inject(new_deps)
|
||||
injector = new(new_deps)
|
||||
injector.inject(Bundler.default_gemfile, Bundler.default_lockfile)
|
||||
end
|
||||
|
||||
def initialize(new_deps)
|
||||
@new_deps = new_deps
|
||||
end
|
||||
|
||||
def inject(gemfile_path, lockfile_path)
|
||||
if Bundler.settings[:frozen]
|
||||
# ensure the lock and Gemfile are synced
|
||||
Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true)
|
||||
# temporarily remove frozen while we inject
|
||||
frozen = Bundler.settings.delete(:frozen)
|
||||
end
|
||||
|
||||
# evaluate the Gemfile we have now
|
||||
builder = Dsl.new
|
||||
builder.eval_gemfile(gemfile_path)
|
||||
|
||||
# don't inject any gems that are already in the Gemfile
|
||||
@new_deps -= builder.dependencies
|
||||
|
||||
# add new deps to the end of the in-memory Gemfile
|
||||
builder.eval_gemfile("injected gems", new_gem_lines) if @new_deps.any?
|
||||
|
||||
# resolve to see if the new deps broke anything
|
||||
definition = builder.to_definition(lockfile_path, {})
|
||||
definition.resolve_remotely!
|
||||
|
||||
# since nothing broke, we can add those gems to the gemfile
|
||||
append_to(gemfile_path) if @new_deps.any?
|
||||
|
||||
# since we resolved successfully, write out the lockfile
|
||||
definition.lock(Bundler.default_lockfile)
|
||||
|
||||
# return an array of the deps that we added
|
||||
return @new_deps
|
||||
ensure
|
||||
Bundler.settings[:frozen] = '1' if frozen
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def new_gem_lines
|
||||
@new_deps.map do |d|
|
||||
%|gem '#{d.name}', '#{d.requirement}'|
|
||||
end.join("\n")
|
||||
end
|
||||
|
||||
def append_to(gemfile_path)
|
||||
gemfile_path.open("a") do |f|
|
||||
f.puts
|
||||
f.puts "# Added at #{Time.now} by #{`whoami`.chomp}:"
|
||||
f.puts new_gem_lines
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,334 @@
|
|||
require 'erb'
|
||||
require 'rubygems/dependency_installer'
|
||||
require 'bundler/parallel_workers'
|
||||
|
||||
module Bundler
|
||||
class Installer < Environment
|
||||
class << self
|
||||
attr_accessor :post_install_messages, :ambiguous_gems
|
||||
|
||||
Installer.post_install_messages = {}
|
||||
Installer.ambiguous_gems = []
|
||||
end
|
||||
|
||||
# Begins the installation process for Bundler.
|
||||
# For more information see the #run method on this class.
|
||||
def self.install(root, definition, options = {})
|
||||
installer = new(root, definition)
|
||||
installer.run(options)
|
||||
installer
|
||||
end
|
||||
|
||||
# Runs the install procedures for a specific Gemfile.
|
||||
#
|
||||
# Firstly, this method will check to see if Bundler.bundle_path exists
|
||||
# and if not then will create it. This is usually the location of gems
|
||||
# on the system, be it RVM or at a system path.
|
||||
#
|
||||
# Secondly, it checks if Bundler has been configured to be "frozen"
|
||||
# Frozen ensures that the Gemfile and the Gemfile.lock file are matching.
|
||||
# This stops a situation where a developer may update the Gemfile but may not run
|
||||
# `bundle install`, which leads to the Gemfile.lock file not being correctly updated.
|
||||
# If this file is not correctly updated then any other developer running
|
||||
# `bundle install` will potentially not install the correct gems.
|
||||
#
|
||||
# Thirdly, Bundler checks if there are any dependencies specified in the Gemfile using
|
||||
# Bundler::Environment#dependencies. If there are no dependencies specified then
|
||||
# Bundler returns a warning message stating so and this method returns.
|
||||
#
|
||||
# Fourthly, Bundler checks if the default lockfile (Gemfile.lock) exists, and if so
|
||||
# then proceeds to set up a defintion based on the default gemfile (Gemfile) and the
|
||||
# default lock file (Gemfile.lock). However, this is not the case if the platform is different
|
||||
# to that which is specified in Gemfile.lock, or if there are any missing specs for the gems.
|
||||
#
|
||||
# Fifthly, Bundler resolves the dependencies either through a cache of gems or by remote.
|
||||
# This then leads into the gems being installed, along with stubs for their executables,
|
||||
# but only if the --binstubs option has been passed or Bundler.options[:bin] has been set
|
||||
# earlier.
|
||||
#
|
||||
# Sixthly, a new Gemfile.lock is created from the installed gems to ensure that the next time
|
||||
# that a user runs `bundle install` they will receive any updates from this process.
|
||||
#
|
||||
# Finally: TODO add documentation for how the standalone process works.
|
||||
def run(options)
|
||||
create_bundle_path
|
||||
|
||||
if Bundler.settings[:frozen]
|
||||
@definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
|
||||
end
|
||||
|
||||
if dependencies.empty?
|
||||
Bundler.ui.warn "The Gemfile specifies no dependencies"
|
||||
lock
|
||||
return
|
||||
end
|
||||
|
||||
if Bundler.default_lockfile.exist? && !options["update"]
|
||||
local = Bundler.ui.silence do
|
||||
begin
|
||||
tmpdef = Definition.build(Bundler.default_gemfile, Bundler.default_lockfile, nil)
|
||||
true unless tmpdef.new_platform? || tmpdef.missing_specs.any?
|
||||
rescue BundlerError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Since we are installing, we can resolve the definition
|
||||
# using remote specs
|
||||
unless local
|
||||
options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
|
||||
end
|
||||
|
||||
# the order that the resolver provides is significant, since
|
||||
# dependencies might actually affect the installation of a gem.
|
||||
# that said, it's a rare situation (other than rake), and parallel
|
||||
# installation is just SO MUCH FASTER. so we let people opt in.
|
||||
jobs = [Bundler.settings[:jobs].to_i-1, 1].max
|
||||
if jobs > 1 && can_install_parallely?
|
||||
install_in_parallel jobs, options[:standalone]
|
||||
else
|
||||
install_sequentially options[:standalone]
|
||||
end
|
||||
|
||||
lock unless Bundler.settings[:frozen]
|
||||
generate_standalone(options[:standalone]) if options[:standalone]
|
||||
end
|
||||
|
||||
def install_gem_from_spec(spec, standalone = false, worker = 0)
|
||||
# Fetch the build settings, if there are any
|
||||
settings = Bundler.settings["build.#{spec.name}"]
|
||||
install_message = nil
|
||||
post_install_message = nil
|
||||
debug_message = nil
|
||||
Bundler.rubygems.with_build_args [settings] do
|
||||
install_message, post_install_message, debug_message = spec.source.install(spec)
|
||||
if install_message.include? 'Installing'
|
||||
Bundler.ui.confirm install_message
|
||||
else
|
||||
Bundler.ui.info install_message
|
||||
end
|
||||
Bundler.ui.debug debug_message if debug_message
|
||||
Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
|
||||
end
|
||||
|
||||
if Bundler.settings[:bin] && standalone
|
||||
generate_standalone_bundler_executable_stubs(spec)
|
||||
elsif Bundler.settings[:bin]
|
||||
generate_bundler_executable_stubs(spec, :force => true)
|
||||
end
|
||||
|
||||
post_install_message
|
||||
rescue Errno::ENOSPC
|
||||
raise Bundler::InstallError, "Your disk is out of space. Free some " \
|
||||
"space to be able to install your bundle."
|
||||
rescue Exception => e
|
||||
# if install hook failed or gem signature is bad, just die
|
||||
raise e if e.is_a?(Bundler::InstallHookError) || e.is_a?(Bundler::SecurityError)
|
||||
|
||||
# other failure, likely a native extension build failure
|
||||
Bundler.ui.info ""
|
||||
Bundler.ui.warn "#{e.class}: #{e.message}"
|
||||
msg = "An error occurred while installing #{spec.name} (#{spec.version}),"
|
||||
msg << " and Bundler cannot continue."
|
||||
|
||||
unless spec.source.options["git"]
|
||||
msg << "\nMake sure that `gem install"
|
||||
msg << " #{spec.name} -v '#{spec.version}'` succeeds before bundling."
|
||||
end
|
||||
Bundler.ui.debug e.backtrace.join("\n")
|
||||
raise Bundler::InstallError, msg
|
||||
end
|
||||
|
||||
def generate_bundler_executable_stubs(spec, options = {})
|
||||
if options[:binstubs_cmd] && spec.executables.empty?
|
||||
options = {}
|
||||
spec.runtime_dependencies.each do |dep|
|
||||
bins = @definition.specs[dep].first.executables
|
||||
options[dep.name] = bins unless bins.empty?
|
||||
end
|
||||
if options.any?
|
||||
Bundler.ui.warn "#{spec.name} has no executables, but you may want " +
|
||||
"one from a gem it depends on."
|
||||
options.each{|name,bins| Bundler.ui.warn " #{name} has: #{bins.join(', ')}" }
|
||||
else
|
||||
Bundler.ui.warn "There are no executables for the gem #{spec.name}."
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
# double-assignment to avoid warnings about variables that will be used by ERB
|
||||
bin_path = bin_path = Bundler.bin_path
|
||||
template = template = File.read(File.expand_path('../templates/Executable', __FILE__))
|
||||
relative_gemfile_path = relative_gemfile_path = Bundler.default_gemfile.relative_path_from(bin_path)
|
||||
ruby_command = ruby_command = Thor::Util.ruby_command
|
||||
|
||||
exists = []
|
||||
spec.executables.each do |executable|
|
||||
next if executable == "bundle"
|
||||
|
||||
binstub_path = "#{bin_path}/#{executable}"
|
||||
if File.exist?(binstub_path) && !options[:force]
|
||||
exists << executable
|
||||
next
|
||||
end
|
||||
|
||||
File.open(binstub_path, 'w', 0777 & ~File.umask) do |f|
|
||||
f.puts ERB.new(template, nil, '-').result(binding)
|
||||
end
|
||||
end
|
||||
|
||||
if options[:binstubs_cmd] && exists.any?
|
||||
case exists.size
|
||||
when 1
|
||||
Bundler.ui.warn "Skipped #{exists[0]} since it already exists."
|
||||
when 2
|
||||
Bundler.ui.warn "Skipped #{exists.join(' and ')} since they already exist."
|
||||
else
|
||||
items = exists[0...-1].empty? ? nil : exists[0...-1].join(', ')
|
||||
skipped = [items, exists[-1]].compact.join(' and ')
|
||||
Bundler.ui.warn "Skipped #{skipped} since they already exist."
|
||||
end
|
||||
Bundler.ui.warn "If you want to overwrite skipped stubs, use --force."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_install_parallely?
|
||||
min_rubygems = "2.0.7"
|
||||
if Bundler.current_ruby.mri? || Bundler.rubygems.provides?(">= #{min_rubygems}")
|
||||
true
|
||||
else
|
||||
Bundler.ui.warn "Rubygems #{Gem::VERSION} is not threadsafe, so your "\
|
||||
"gems must be installed one at a time. Upgrade to Rubygems " \
|
||||
"#{min_rubygems} or higher to enable parallel gem installation."
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def generate_standalone_bundler_executable_stubs(spec)
|
||||
# double-assignment to avoid warnings about variables that will be used by ERB
|
||||
bin_path = Bundler.bin_path
|
||||
template = File.read(File.expand_path('../templates/Executable.standalone', __FILE__))
|
||||
ruby_command = ruby_command = Thor::Util.ruby_command
|
||||
|
||||
spec.executables.each do |executable|
|
||||
next if executable == "bundle"
|
||||
standalone_path = standalone_path = Pathname(Bundler.settings[:path]).expand_path.relative_path_from(bin_path)
|
||||
executable_path = executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path)
|
||||
File.open "#{bin_path}/#{executable}", 'w', 0755 do |f|
|
||||
f.puts ERB.new(template, nil, '-').result(binding)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def generate_standalone(groups)
|
||||
standalone_path = Bundler.settings[:path]
|
||||
bundler_path = File.join(standalone_path, "bundler")
|
||||
FileUtils.mkdir_p(bundler_path)
|
||||
|
||||
paths = []
|
||||
|
||||
if groups.empty?
|
||||
specs = @definition.requested_specs
|
||||
else
|
||||
specs = @definition.specs_for groups.map { |g| g.to_sym }
|
||||
end
|
||||
|
||||
specs.each do |spec|
|
||||
next if spec.name == "bundler"
|
||||
next if spec.require_paths.nil? # builtin gems
|
||||
|
||||
spec.require_paths.each do |path|
|
||||
full_path = File.join(spec.full_gem_path, path)
|
||||
gem_path = Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path))
|
||||
paths << gem_path.to_s.sub("#{Bundler.ruby_version.engine}/#{RbConfig::CONFIG['ruby_version']}", '#{ruby_engine}/#{ruby_version}')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
File.open File.join(bundler_path, "setup.rb"), "w" do |file|
|
||||
file.puts "require 'rbconfig'"
|
||||
file.puts "# ruby 1.8.7 doesn't define RUBY_ENGINE"
|
||||
file.puts "ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'"
|
||||
file.puts "ruby_version = RbConfig::CONFIG[\"ruby_version\"]"
|
||||
file.puts "path = File.expand_path('..', __FILE__)"
|
||||
paths.each do |path|
|
||||
file.puts %{$:.unshift File.expand_path("\#{path}/#{path}")}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def install_sequentially(standalone)
|
||||
specs.each do |spec|
|
||||
message = install_gem_from_spec spec, standalone, 0
|
||||
if message
|
||||
Installer.post_install_messages[spec.name] = message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def install_in_parallel(size, standalone)
|
||||
name2spec = {}
|
||||
remains = {}
|
||||
enqueued = {}
|
||||
specs.each do |spec|
|
||||
name2spec[spec.name] = spec
|
||||
remains[spec.name] = true
|
||||
end
|
||||
|
||||
worker_pool = ParallelWorkers.worker_pool size, lambda { |name, worker|
|
||||
spec = name2spec[name]
|
||||
message = install_gem_from_spec spec, standalone, worker
|
||||
{ :name => spec.name, :post_install => message }
|
||||
}
|
||||
|
||||
# Keys in the remains hash represent uninstalled gems specs.
|
||||
# We enqueue all gem specs that do not have any dependencies.
|
||||
# Later we call this lambda again to install specs that depended on
|
||||
# previously installed specifications. We continue until all specs
|
||||
# are installed.
|
||||
enqueue_remaining_specs = lambda do
|
||||
remains.keys.each do |name|
|
||||
next if enqueued[name]
|
||||
spec = name2spec[name]
|
||||
if ready_to_install?(spec, remains)
|
||||
worker_pool.enq name
|
||||
enqueued[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
enqueue_remaining_specs.call
|
||||
|
||||
until remains.empty?
|
||||
message = worker_pool.deq
|
||||
remains.delete message[:name]
|
||||
if message[:post_install]
|
||||
Installer.post_install_messages[message[:name]] = message[:post_install]
|
||||
end
|
||||
enqueue_remaining_specs.call
|
||||
end
|
||||
message
|
||||
ensure
|
||||
worker_pool && worker_pool.stop
|
||||
end
|
||||
|
||||
# We only want to install a gem spec if all its dependencies are met.
|
||||
# If the dependency is no longer in the `remains` hash then it has been met.
|
||||
# If a dependency is only development or is self referential it can be ignored.
|
||||
def ready_to_install?(spec, remains)
|
||||
spec.dependencies.none? do |dep|
|
||||
next if dep.type == :development || dep.name == spec.name
|
||||
remains[dep.name]
|
||||
end
|
||||
end
|
||||
|
||||
def create_bundle_path
|
||||
Bundler.mkdir_p(Bundler.bundle_path.to_s) unless Bundler.bundle_path.exist?
|
||||
rescue Errno::EEXIST
|
||||
raise PathError, "Could not install to path `#{Bundler.settings[:path]}` " +
|
||||
"because of an invalid symlink. Remove the symlink so the directory can be created."
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,83 @@
|
|||
require "uri"
|
||||
require "rubygems/spec_fetcher"
|
||||
require "bundler/match_platform"
|
||||
|
||||
module Bundler
|
||||
class LazySpecification
|
||||
include MatchPlatform
|
||||
|
||||
attr_reader :name, :version, :dependencies, :platform
|
||||
attr_accessor :source, :source_uri
|
||||
|
||||
def initialize(name, version, platform, source = nil)
|
||||
@name = name
|
||||
@version = version
|
||||
@dependencies = []
|
||||
@platform = platform
|
||||
@source = source
|
||||
@specification = nil
|
||||
end
|
||||
|
||||
def full_name
|
||||
if platform == Gem::Platform::RUBY or platform.nil? then
|
||||
"#{@name}-#{@version}"
|
||||
else
|
||||
"#{@name}-#{@version}-#{platform}"
|
||||
end
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
identifier == other.identifier
|
||||
end
|
||||
|
||||
def satisfies?(dependency)
|
||||
@name == dependency.name && dependency.requirement.satisfied_by?(Gem::Version.new(@version))
|
||||
end
|
||||
|
||||
def to_lock
|
||||
if platform == Gem::Platform::RUBY or platform.nil?
|
||||
out = " #{name} (#{version})\n"
|
||||
else
|
||||
out = " #{name} (#{version}-#{platform})\n"
|
||||
end
|
||||
|
||||
dependencies.sort_by {|d| d.to_s }.each do |dep|
|
||||
next if dep.type == :development
|
||||
out << " #{dep.to_lock}\n"
|
||||
end
|
||||
|
||||
out
|
||||
end
|
||||
|
||||
def __materialize__
|
||||
@specification = source.specs.search(Gem::Dependency.new(name, version)).last
|
||||
end
|
||||
|
||||
def respond_to?(*args)
|
||||
super || @specification.respond_to?(*args)
|
||||
end
|
||||
|
||||
def to_s
|
||||
@__to_s ||= "#{name} (#{version})"
|
||||
end
|
||||
|
||||
def identifier
|
||||
@__identifier ||= [name, version, source, platform, dependencies].hash
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def to_ary
|
||||
nil
|
||||
end
|
||||
|
||||
def method_missing(method, *args, &blk)
|
||||
raise "LazySpecification has not been materialized yet (calling :#{method} #{args.inspect})" unless @specification
|
||||
|
||||
return super unless respond_to?(method)
|
||||
|
||||
@specification.send(method, *args, &blk)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,157 @@
|
|||
require "strscan"
|
||||
|
||||
# Some versions of the Bundler 1.1 RC series introduced corrupted
|
||||
# lockfiles. There were two major problems:
|
||||
#
|
||||
# * multiple copies of the same GIT section appeared in the lockfile
|
||||
# * when this happened, those sections got multiple copies of gems
|
||||
# in those sections.
|
||||
#
|
||||
# As a result, Bundler 1.1 contains code that fixes the earlier
|
||||
# corruption. We will remove this fix-up code in Bundler 1.2.
|
||||
|
||||
module Bundler
|
||||
class LockfileParser
|
||||
attr_reader :sources, :dependencies, :specs, :platforms
|
||||
|
||||
DEPENDENCIES = "DEPENDENCIES"
|
||||
PLATFORMS = "PLATFORMS"
|
||||
GIT = "GIT"
|
||||
GEM = "GEM"
|
||||
PATH = "PATH"
|
||||
SPECS = " specs:"
|
||||
OPTIONS = /^ ([a-z]+): (.*)$/i
|
||||
|
||||
def initialize(lockfile)
|
||||
@platforms = []
|
||||
@sources = []
|
||||
@dependencies = []
|
||||
@state = :source
|
||||
@specs = {}
|
||||
|
||||
@rubygems_aggregate = Source::Rubygems.new
|
||||
|
||||
if lockfile.match(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
|
||||
raise LockfileError, "Your Gemfile.lock contains merge conflicts.\n" \
|
||||
"Run `git checkout HEAD -- Gemfile.lock` first to get a clean lock."
|
||||
end
|
||||
|
||||
lockfile.split(/(?:\r?\n)+/).each do |line|
|
||||
if line == DEPENDENCIES
|
||||
@state = :dependency
|
||||
elsif line == PLATFORMS
|
||||
@state = :platform
|
||||
else
|
||||
send("parse_#{@state}", line)
|
||||
end
|
||||
end
|
||||
@sources << @rubygems_aggregate
|
||||
@specs = @specs.values
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
TYPES = {
|
||||
"GIT" => Bundler::Source::Git,
|
||||
"GEM" => Bundler::Source::Rubygems,
|
||||
"PATH" => Bundler::Source::Path
|
||||
}
|
||||
|
||||
def parse_source(line)
|
||||
case line
|
||||
when GIT, GEM, PATH
|
||||
@current_source = nil
|
||||
@opts, @type = {}, line
|
||||
when SPECS
|
||||
case @type
|
||||
when "PATH"
|
||||
@current_source = TYPES[@type].from_lock(@opts)
|
||||
@sources << @current_source
|
||||
when "GIT"
|
||||
@current_source = TYPES[@type].from_lock(@opts)
|
||||
# Strip out duplicate GIT sections
|
||||
if @type == "GIT" && @sources.include?(@current_source)
|
||||
@current_source = @sources.find { |s| s == @current_source }
|
||||
else
|
||||
@sources << @current_source
|
||||
end
|
||||
when "GEM"
|
||||
Array(@opts["remote"]).each do |url|
|
||||
@rubygems_aggregate.add_remote(url)
|
||||
end
|
||||
@current_source = @rubygems_aggregate
|
||||
end
|
||||
when OPTIONS
|
||||
value = $2
|
||||
value = true if value == "true"
|
||||
value = false if value == "false"
|
||||
|
||||
key = $1
|
||||
|
||||
if @opts[key]
|
||||
@opts[key] = Array(@opts[key])
|
||||
@opts[key] << value
|
||||
else
|
||||
@opts[key] = value
|
||||
end
|
||||
else
|
||||
parse_spec(line)
|
||||
end
|
||||
end
|
||||
|
||||
NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
|
||||
NAME_VERSION_2 = %r{^ {2}#{NAME_VERSION}(!)?$}
|
||||
NAME_VERSION_4 = %r{^ {4}#{NAME_VERSION}$}
|
||||
NAME_VERSION_6 = %r{^ {6}#{NAME_VERSION}$}
|
||||
|
||||
def parse_dependency(line)
|
||||
if line =~ NAME_VERSION_2
|
||||
name, version, pinned = $1, $2, $4
|
||||
version = version.split(",").map { |d| d.strip } if version
|
||||
|
||||
dep = Bundler::Dependency.new(name, version)
|
||||
|
||||
if pinned && dep.name != 'bundler'
|
||||
spec = @specs.find {|k, v| v.name == dep.name }
|
||||
dep.source = spec.last.source if spec
|
||||
|
||||
# Path sources need to know what the default name / version
|
||||
# to use in the case that there are no gemspecs present. A fake
|
||||
# gemspec is created based on the version set on the dependency
|
||||
# TODO: Use the version from the spec instead of from the dependency
|
||||
if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path)
|
||||
dep.source.name = name
|
||||
dep.source.version = $1
|
||||
end
|
||||
end
|
||||
|
||||
@dependencies << dep
|
||||
end
|
||||
end
|
||||
|
||||
def parse_spec(line)
|
||||
if line =~ NAME_VERSION_4
|
||||
name, version = $1, Gem::Version.new($2)
|
||||
platform = $3 ? Gem::Platform.new($3) : Gem::Platform::RUBY
|
||||
@current_spec = LazySpecification.new(name, version, platform)
|
||||
@current_spec.source = @current_source
|
||||
|
||||
# Avoid introducing multiple copies of the same spec (caused by
|
||||
# duplicate GIT sections)
|
||||
@specs[@current_spec.identifier] ||= @current_spec
|
||||
elsif line =~ NAME_VERSION_6
|
||||
name, version = $1, $2
|
||||
version = version.split(',').map { |d| d.strip } if version
|
||||
dep = Gem::Dependency.new(name, version)
|
||||
@current_spec.dependencies << dep
|
||||
end
|
||||
end
|
||||
|
||||
def parse_platform(line)
|
||||
if line =~ /^ (.*)$/
|
||||
@platforms << Gem::Platform.new($1)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
require 'bundler/gem_helpers'
|
||||
|
||||
module Bundler
|
||||
module MatchPlatform
|
||||
include GemHelpers
|
||||
|
||||
def match_platform(p)
|
||||
Gem::Platform::RUBY == platform or
|
||||
platform.nil? or p == platform or
|
||||
generic(Gem::Platform.new(platform)) == p
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
require 'thread'
|
||||
|
||||
require "bundler/parallel_workers/worker"
|
||||
|
||||
module Bundler
|
||||
module ParallelWorkers
|
||||
autoload :UnixWorker, "bundler/parallel_workers/unix_worker"
|
||||
autoload :ThreadWorker, "bundler/parallel_workers/thread_worker"
|
||||
|
||||
def self.worker_pool(size, job)
|
||||
if Bundler.current_ruby.mswin? || Bundler.current_ruby.jruby? || Bundler.current_ruby.rbx?
|
||||
ThreadWorker.new(size, job)
|
||||
else
|
||||
UnixWorker.new(size, job)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
module Bundler
|
||||
module ParallelWorkers
|
||||
class ThreadWorker < Worker
|
||||
|
||||
private
|
||||
|
||||
# On platforms where fork is not available
|
||||
# use Threads for parallely downloading gems
|
||||
#
|
||||
# @param size [Integer] Size of thread worker pool
|
||||
# @param func [Proc] Job to be run inside thread worker pool
|
||||
def prepare_workers(size, func)
|
||||
@threads = size.times.map do |i|
|
||||
Thread.start do
|
||||
loop do
|
||||
obj = @request_queue.deq
|
||||
break if obj.equal? POISON
|
||||
begin
|
||||
@response_queue.enq func.call(obj, i)
|
||||
rescue Exception => e
|
||||
@response_queue.enq(WrappedException.new(e))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,101 @@
|
|||
module Bundler
|
||||
module ParallelWorkers
|
||||
# UnixWorker is used only on platforms where fork is available. The way
|
||||
# this code works is, it forks a preconfigured number of workers and then
|
||||
# It starts preconfigured number of threads that write to the connected pipe.
|
||||
class UnixWorker < Worker
|
||||
|
||||
class JobHandler < Struct.new(:pid, :io_r, :io_w)
|
||||
def work(obj)
|
||||
Marshal.dump obj, io_w
|
||||
Marshal.load io_r
|
||||
rescue IOError, Errno::EPIPE
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(size, job)
|
||||
# Close the persistent connections for the main thread before forking
|
||||
Net::HTTP::Persistent.new('bundler', :ENV).shutdown
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Start forked workers for downloading gems. This version of worker
|
||||
# is only used on platforms where fork is available.
|
||||
#
|
||||
# @param size [Integer] Size of worker pool
|
||||
# @param func [Proc] Job that should be executed in the worker
|
||||
def prepare_workers(size, func)
|
||||
@workers = size.times.map do |num|
|
||||
child_read, parent_write = IO.pipe
|
||||
parent_read, child_write = IO.pipe
|
||||
|
||||
pid = Process.fork do
|
||||
begin
|
||||
parent_read.close
|
||||
parent_write.close
|
||||
|
||||
while !child_read.eof?
|
||||
obj = Marshal.load child_read
|
||||
Marshal.dump func.call(obj, num), child_write
|
||||
end
|
||||
rescue Exception => e
|
||||
begin
|
||||
Marshal.dump WrappedException.new(e), child_write
|
||||
rescue Errno::EPIPE
|
||||
nil
|
||||
end
|
||||
ensure
|
||||
child_read.close
|
||||
child_write.close
|
||||
end
|
||||
end
|
||||
|
||||
child_read.close
|
||||
child_write.close
|
||||
JobHandler.new pid, parent_read, parent_write
|
||||
end
|
||||
end
|
||||
|
||||
# Start the threads whose job is basically to wait for incoming messages
|
||||
# on request queue and write that message to the connected pipe. Also retrieve
|
||||
# messages from child worker via connected pipe and write the message to response queue
|
||||
#
|
||||
# @param size [Integer] Number of threads to be started
|
||||
def prepare_threads(size)
|
||||
@threads = size.times.map do |i|
|
||||
Thread.start do
|
||||
worker = @workers[i]
|
||||
loop do
|
||||
obj = @request_queue.deq
|
||||
break if obj.equal? POISON
|
||||
@response_queue.enq worker.work(obj)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Kill the forked workers by sending SIGINT to them
|
||||
def stop_workers
|
||||
@workers.each do |worker|
|
||||
worker.io_r.close unless worker.io_r.closed?
|
||||
worker.io_w.close unless worker.io_w.closed?
|
||||
begin
|
||||
Process.kill :INT, worker.pid
|
||||
rescue Errno::ESRCH
|
||||
nil
|
||||
end
|
||||
end
|
||||
@workers.each do |worker|
|
||||
begin
|
||||
Process.waitpid worker.pid
|
||||
rescue Errno::ECHILD
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,69 @@
|
|||
module Bundler
|
||||
module ParallelWorkers
|
||||
class Worker
|
||||
POISON = Object.new
|
||||
|
||||
class WrappedException < StandardError
|
||||
attr_reader :exception
|
||||
def initialize(exn)
|
||||
@exception = exn
|
||||
end
|
||||
end
|
||||
|
||||
# Creates a worker pool of specified size
|
||||
#
|
||||
# @param size [Integer] Size of pool
|
||||
# @param func [Proc] job to run in inside the worker pool
|
||||
def initialize(size, func)
|
||||
@request_queue = Queue.new
|
||||
@response_queue = Queue.new
|
||||
prepare_workers size, func
|
||||
prepare_threads size
|
||||
trap("INT") { @threads.each {|i| i.exit }; stop_workers; exit 1 }
|
||||
end
|
||||
|
||||
# Enqueue a request to be executed in the worker pool
|
||||
#
|
||||
# @param obj [String] mostly it is name of spec that should be downloaded
|
||||
def enq(obj)
|
||||
@request_queue.enq obj
|
||||
end
|
||||
|
||||
# Retrieves results of job function being executed in worker pool
|
||||
def deq
|
||||
result = @response_queue.deq
|
||||
if result.is_a?(WrappedException)
|
||||
raise result.exception
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
# Stop the forked workers and started threads
|
||||
def stop
|
||||
stop_threads
|
||||
stop_workers
|
||||
end
|
||||
|
||||
private
|
||||
# Stop the worker threads by sending a poison object down the request queue
|
||||
# so as worker threads after retrieving it, shut themselves down
|
||||
def stop_threads
|
||||
@threads.each do
|
||||
@request_queue.enq POISON
|
||||
end
|
||||
@threads.each do |thread|
|
||||
thread.join
|
||||
end
|
||||
end
|
||||
|
||||
# To be overridden by child classes
|
||||
def prepare_threads(size)
|
||||
end
|
||||
|
||||
# To be overridden by child classes
|
||||
def stop_workers
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# Psych could be a gem, so try to ask for it
|
||||
begin
|
||||
gem 'psych'
|
||||
rescue LoadError
|
||||
end if defined?(gem)
|
||||
|
||||
# Psych could just be in the stdlib
|
||||
# but it's too late if Syck is already loaded
|
||||
begin
|
||||
require 'psych' unless defined?(Syck)
|
||||
rescue LoadError
|
||||
# Apparently Psych wasn't available. Oh well.
|
||||
end
|
||||
|
||||
# At least load the YAML stdlib, whatever that may be
|
||||
require 'yaml' unless defined?(YAML.dump)
|
||||
|
||||
module Bundler
|
||||
# On encountering invalid YAML,
|
||||
# Psych raises Psych::SyntaxError
|
||||
if defined?(::Psych::SyntaxError)
|
||||
YamlSyntaxError = ::Psych::SyntaxError
|
||||
else # Syck raises ArgumentError
|
||||
YamlSyntaxError = ::ArgumentError
|
||||
end
|
||||
end
|
|
@ -0,0 +1,57 @@
|
|||
require "uri"
|
||||
require "rubygems/spec_fetcher"
|
||||
|
||||
module Bundler
|
||||
# Represents a lazily loaded gem specification, where the full specification
|
||||
# is on the source server in rubygems' "quick" index. The proxy object is to
|
||||
# be seeded with what we're given from the source's abbreviated index - the
|
||||
# full specification will only be fetched when necessary.
|
||||
class RemoteSpecification
|
||||
include MatchPlatform
|
||||
|
||||
attr_reader :name, :version, :platform
|
||||
attr_accessor :source, :source_uri
|
||||
|
||||
def initialize(name, version, platform, spec_fetcher)
|
||||
@name = name
|
||||
@version = version
|
||||
@platform = platform
|
||||
@spec_fetcher = spec_fetcher
|
||||
end
|
||||
|
||||
# Needed before installs, since the arch matters then and quick
|
||||
# specs don't bother to include the arch in the platform string
|
||||
def fetch_platform
|
||||
@platform = _remote_specification.platform
|
||||
end
|
||||
|
||||
def full_name
|
||||
if platform == Gem::Platform::RUBY or platform.nil? then
|
||||
"#{@name}-#{@version}"
|
||||
else
|
||||
"#{@name}-#{@version}-#{platform}"
|
||||
end
|
||||
end
|
||||
|
||||
# Because Rubyforge cannot be trusted to provide valid specifications
|
||||
# once the remote gem is downloaded, the backend specification will
|
||||
# be swapped out.
|
||||
def __swap__(spec)
|
||||
@specification = spec
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _remote_specification
|
||||
@specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
|
||||
end
|
||||
|
||||
def method_missing(method, *args, &blk)
|
||||
if Gem::Specification.new.respond_to?(method)
|
||||
_remote_specification.send(method, *args, &blk)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,534 @@
|
|||
require 'set'
|
||||
# This is the latest iteration of the gem dependency resolving algorithm. As of now,
|
||||
# it can resolve (as a success or failure) any set of gem dependencies we throw at it
|
||||
# in a reasonable amount of time. The most iterations I've seen it take is about 150.
|
||||
# The actual implementation of the algorithm is not as good as it could be yet, but that
|
||||
# can come later.
|
||||
|
||||
# Extending Gem classes to add necessary tracking information
|
||||
module Gem
|
||||
class Specification
|
||||
def required_by
|
||||
@required_by ||= []
|
||||
end
|
||||
end
|
||||
class Dependency
|
||||
def required_by
|
||||
@required_by ||= []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Bundler
|
||||
class Resolver
|
||||
|
||||
ALL = Bundler::Dependency::PLATFORM_MAP.values.uniq.freeze
|
||||
|
||||
class SpecGroup < Array
|
||||
include GemHelpers
|
||||
|
||||
attr_reader :activated, :required_by
|
||||
|
||||
def initialize(a)
|
||||
super
|
||||
@required_by = []
|
||||
@activated = []
|
||||
@dependencies = nil
|
||||
@specs = {}
|
||||
|
||||
ALL.each do |p|
|
||||
@specs[p] = reverse.find { |s| s.match_platform(p) }
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_copy(o)
|
||||
super
|
||||
@required_by = o.required_by.dup
|
||||
@activated = o.activated.dup
|
||||
end
|
||||
|
||||
def to_specs
|
||||
specs = {}
|
||||
|
||||
@activated.each do |p|
|
||||
if s = @specs[p]
|
||||
platform = generic(Gem::Platform.new(s.platform))
|
||||
next if specs[platform]
|
||||
|
||||
lazy_spec = LazySpecification.new(name, version, platform, source)
|
||||
lazy_spec.dependencies.replace s.dependencies
|
||||
specs[platform] = lazy_spec
|
||||
end
|
||||
end
|
||||
specs.values
|
||||
end
|
||||
|
||||
def activate_platform(platform)
|
||||
unless @activated.include?(platform)
|
||||
@activated << platform
|
||||
return __dependencies[platform] || []
|
||||
end
|
||||
[]
|
||||
end
|
||||
|
||||
def name
|
||||
@name ||= first.name
|
||||
end
|
||||
|
||||
def version
|
||||
@version ||= first.version
|
||||
end
|
||||
|
||||
def source
|
||||
@source ||= first.source
|
||||
end
|
||||
|
||||
def for?(platform)
|
||||
@specs[platform]
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{name} (#{version})"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def __dependencies
|
||||
@dependencies ||= begin
|
||||
dependencies = {}
|
||||
ALL.each do |p|
|
||||
if spec = @specs[p]
|
||||
dependencies[p] = []
|
||||
spec.dependencies.each do |dep|
|
||||
next if dep.type == :development
|
||||
dependencies[p] << DepProxy.new(dep, p)
|
||||
end
|
||||
end
|
||||
end
|
||||
dependencies
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :errors, :started_at, :iteration_rate, :iteration_counter
|
||||
|
||||
# Figures out the best possible configuration of gems that satisfies
|
||||
# the list of passed dependencies and any child dependencies without
|
||||
# causing any gem activation errors.
|
||||
#
|
||||
# ==== Parameters
|
||||
# *dependencies<Gem::Dependency>:: The list of dependencies to resolve
|
||||
#
|
||||
# ==== Returns
|
||||
# <GemBundle>,nil:: If the list of dependencies can be resolved, a
|
||||
# collection of gemspecs is returned. Otherwise, nil is returned.
|
||||
def self.resolve(requirements, index, source_requirements = {}, base = [])
|
||||
Bundler.ui.info "Resolving dependencies...", false
|
||||
base = SpecSet.new(base) unless base.is_a?(SpecSet)
|
||||
resolver = new(index, source_requirements, base)
|
||||
result = resolver.start(requirements)
|
||||
Bundler.ui.info "" # new line now that dots are done
|
||||
SpecSet.new(result)
|
||||
rescue => e
|
||||
Bundler.ui.info "" # new line before the error
|
||||
raise e
|
||||
end
|
||||
|
||||
def initialize(index, source_requirements, base)
|
||||
@errors = {}
|
||||
@base = base
|
||||
@index = index
|
||||
@deps_for = {}
|
||||
@missing_gems = Hash.new(0)
|
||||
@source_requirements = source_requirements
|
||||
@iteration_counter = 0
|
||||
@started_at = Time.now
|
||||
end
|
||||
|
||||
def debug
|
||||
if ENV['DEBUG_RESOLVER']
|
||||
debug_info = yield
|
||||
debug_info = debug_info.inspect unless debug_info.is_a?(String)
|
||||
$stderr.puts debug_info
|
||||
end
|
||||
end
|
||||
|
||||
def successify(activated)
|
||||
activated.values.map { |s| s.to_specs }.flatten.compact
|
||||
end
|
||||
|
||||
def start(reqs)
|
||||
activated = {}
|
||||
@gems_size = Hash[reqs.map { |r| [r, gems_size(r)] }]
|
||||
|
||||
resolve(reqs, activated)
|
||||
end
|
||||
|
||||
class State < Struct.new(:reqs, :activated, :requirement, :possibles, :depth, :conflicts)
|
||||
def name
|
||||
requirement.name
|
||||
end
|
||||
end
|
||||
|
||||
def handle_conflict(current, states, existing=nil)
|
||||
until current.nil? && existing.nil?
|
||||
current_state = find_state(current, states)
|
||||
existing_state = find_state(existing, states)
|
||||
return current if state_any?(current_state)
|
||||
return existing if state_any?(existing_state)
|
||||
existing = existing.required_by.last if existing
|
||||
current = current.required_by.last if current
|
||||
end
|
||||
end
|
||||
|
||||
def state_any?(state)
|
||||
state && state.possibles.any?
|
||||
end
|
||||
|
||||
def find_state(current, states)
|
||||
states.detect { |i| current && current.name == i.name }
|
||||
end
|
||||
|
||||
def other_possible?(conflict, states)
|
||||
return unless conflict
|
||||
state = states.detect { |i| i.name == conflict.name }
|
||||
state && state.possibles.any?
|
||||
end
|
||||
|
||||
def find_conflict_state(conflict, states)
|
||||
return unless conflict
|
||||
until states.empty? do
|
||||
state = states.pop
|
||||
return state if conflict.name == state.name
|
||||
end
|
||||
end
|
||||
|
||||
def activate_gem(reqs, activated, requirement, current)
|
||||
requirement.required_by.replace current.required_by
|
||||
requirement.required_by << current
|
||||
activated[requirement.name] = requirement
|
||||
|
||||
debug { " Activating: #{requirement.name} (#{requirement.version})" }
|
||||
debug { requirement.required_by.map { |d| " * #{d.name} (#{d.requirement})" }.join("\n") }
|
||||
|
||||
dependencies = requirement.activate_platform(current.__platform)
|
||||
|
||||
debug { " Dependencies"}
|
||||
dependencies.each do |dep|
|
||||
next if dep.type == :development
|
||||
dep.required_by.replace(current.required_by)
|
||||
dep.required_by << current
|
||||
@gems_size[dep] ||= gems_size(dep)
|
||||
reqs << dep
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_for_conflict(state)
|
||||
raise version_conflict if state.nil? || state.possibles.empty?
|
||||
reqs, activated, depth, conflicts = state.reqs.dup, state.activated.dup, state.depth, state.conflicts.dup
|
||||
requirement = state.requirement
|
||||
possible = state.possibles.pop
|
||||
|
||||
activate_gem(reqs, activated, possible, requirement)
|
||||
|
||||
return reqs, activated, depth, conflicts
|
||||
end
|
||||
|
||||
def resolve_conflict(current, states)
|
||||
# Find the state where the conflict has occurred
|
||||
state = find_conflict_state(current, states)
|
||||
|
||||
debug { " -> Going to: #{current.name} state" } if current
|
||||
|
||||
# Resolve the conflicts by rewinding the state
|
||||
# when the conflicted gem was activated
|
||||
reqs, activated, depth, conflicts = resolve_for_conflict(state)
|
||||
|
||||
# Keep the state around if it still has other possibilities
|
||||
states << state unless state.possibles.empty?
|
||||
clear_search_cache
|
||||
|
||||
return reqs, activated, depth, conflicts
|
||||
end
|
||||
|
||||
def resolve(reqs, activated)
|
||||
states = []
|
||||
depth = 0
|
||||
conflicts = Set.new
|
||||
|
||||
until reqs.empty?
|
||||
|
||||
indicate_progress
|
||||
|
||||
debug { print "\e[2J\e[f" ; "==== Iterating ====\n\n" }
|
||||
|
||||
reqs = reqs.sort_by do |a|
|
||||
[ activated[a.name] ? 0 : 1,
|
||||
a.requirement.prerelease? ? 0 : 1,
|
||||
@errors[a.name] ? 0 : 1,
|
||||
activated[a.name] ? 0 : @gems_size[a] ]
|
||||
end
|
||||
|
||||
debug { "Activated:\n" + activated.values.map {|a| " #{a}" }.join("\n") }
|
||||
debug { "Requirements:\n" + reqs.map {|r| " #{r}"}.join("\n") }
|
||||
|
||||
current = reqs.shift
|
||||
|
||||
$stderr.puts "#{' ' * depth}#{current}" if ENV['DEBUG_RESOLVER_TREE']
|
||||
|
||||
debug { "Attempting:\n #{current}"}
|
||||
|
||||
existing = activated[current.name]
|
||||
|
||||
|
||||
if existing || current.name == 'bundler'
|
||||
# Force the current
|
||||
if current.name == 'bundler' && !existing
|
||||
existing = search(DepProxy.new(Gem::Dependency.new('bundler', VERSION), Gem::Platform::RUBY)).first
|
||||
raise GemNotFound, %Q{Bundler could not find gem "bundler" (#{VERSION})} unless existing
|
||||
existing.required_by << existing
|
||||
activated['bundler'] = existing
|
||||
end
|
||||
|
||||
if current.requirement.satisfied_by?(existing.version)
|
||||
debug { " * [SUCCESS] Already activated" }
|
||||
@errors.delete(existing.name)
|
||||
dependencies = existing.activate_platform(current.__platform)
|
||||
reqs.concat dependencies
|
||||
|
||||
dependencies.each do |dep|
|
||||
next if dep.type == :development
|
||||
@gems_size[dep] ||= gems_size(dep)
|
||||
end
|
||||
|
||||
depth += 1
|
||||
next
|
||||
else
|
||||
debug { " * [FAIL] Already activated" }
|
||||
@errors[existing.name] = [existing, current]
|
||||
|
||||
conflicts << current.name
|
||||
|
||||
parent = current.required_by.last
|
||||
if existing.respond_to?(:required_by)
|
||||
parent = handle_conflict(current, states, existing.required_by[-2]) unless other_possible?(parent, states)
|
||||
else
|
||||
parent = handle_conflict(current, states) unless other_possible?(parent, states)
|
||||
end
|
||||
|
||||
if parent.nil? && !conflicts.empty?
|
||||
parent = states.reverse.detect { |i| conflicts.include?(i.name) && state_any?(i)}
|
||||
end
|
||||
|
||||
raise version_conflict if parent.nil? || parent.name == 'bundler'
|
||||
|
||||
reqs, activated, depth, conflicts = resolve_conflict(parent, states)
|
||||
end
|
||||
else
|
||||
matching_versions = search(current)
|
||||
|
||||
# If we found no versions that match the current requirement
|
||||
if matching_versions.empty?
|
||||
# If this is a top-level Gemfile requirement
|
||||
if current.required_by.empty?
|
||||
if base = @base[current.name] and !base.empty?
|
||||
version = base.first.version
|
||||
message = "You have requested:\n" \
|
||||
" #{current.name} #{current.requirement}\n\n" \
|
||||
"The bundle currently has #{current.name} locked at #{version}.\n" \
|
||||
"Try running `bundle update #{current.name}`"
|
||||
elsif current.source
|
||||
name = current.name
|
||||
versions = @source_requirements[name][name].map { |s| s.version }
|
||||
message = "Could not find gem '#{current}' in #{current.source}.\n"
|
||||
if versions.any?
|
||||
message << "Source contains '#{name}' at: #{versions.join(', ')}"
|
||||
else
|
||||
message << "Source does not contain any versions of '#{current}'"
|
||||
end
|
||||
else
|
||||
message = "Could not find gem '#{current}' "
|
||||
if @index.source_types.include?(Bundler::Source::Rubygems)
|
||||
message << "in any of the gem sources listed in your Gemfile."
|
||||
else
|
||||
message << "in the gems available on this machine."
|
||||
end
|
||||
end
|
||||
raise GemNotFound, message
|
||||
# This is not a top-level Gemfile requirement
|
||||
else
|
||||
@errors[current.name] = [nil, current]
|
||||
parent = handle_conflict(current, states)
|
||||
reqs, activated, depth = resolve_conflict(parent, states)
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
state = State.new(reqs.dup, activated.dup, current, matching_versions, depth, conflicts)
|
||||
states << state
|
||||
requirement = state.possibles.pop
|
||||
activate_gem(reqs, activated, requirement, current)
|
||||
end
|
||||
end
|
||||
successify(activated)
|
||||
end
|
||||
|
||||
def gems_size(dep)
|
||||
search(dep).size
|
||||
end
|
||||
|
||||
def clear_search_cache
|
||||
@deps_for = {}
|
||||
end
|
||||
|
||||
def search(dep)
|
||||
if base = @base[dep.name] and base.any?
|
||||
reqs = [dep.requirement.as_list, base.first.version.to_s].flatten.compact
|
||||
d = Gem::Dependency.new(base.first.name, *reqs)
|
||||
else
|
||||
d = dep.dep
|
||||
end
|
||||
|
||||
@deps_for[d.hash] ||= begin
|
||||
index = @source_requirements[d.name] || @index
|
||||
results = index.search(d, @base[d.name])
|
||||
|
||||
if results.any?
|
||||
version = results.first.version
|
||||
nested = [[]]
|
||||
results.each do |spec|
|
||||
if spec.version != version
|
||||
nested << []
|
||||
version = spec.version
|
||||
end
|
||||
nested.last << spec
|
||||
end
|
||||
deps = nested.map{|a| SpecGroup.new(a) }.select{|sg| sg.for?(dep.__platform) }
|
||||
else
|
||||
deps = []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def clean_req(req)
|
||||
if req.to_s.include?(">= 0")
|
||||
req.to_s.gsub(/ \(.*?\)$/, '')
|
||||
else
|
||||
req.to_s.gsub(/\, (runtime|development)\)$/, ')')
|
||||
end
|
||||
end
|
||||
|
||||
def version_conflict
|
||||
VersionConflict.new(errors.keys, error_message)
|
||||
end
|
||||
|
||||
# For a given conflicted requirement, print out what exactly went wrong
|
||||
def gem_message(requirement, required_by=[])
|
||||
m = ""
|
||||
|
||||
# A requirement that is required by itself is actually in the Gemfile, and does
|
||||
# not "depend on" itself
|
||||
if requirement.required_by.first && requirement.required_by.first.name != requirement.name
|
||||
dependency_tree(m, required_by)
|
||||
m << "#{clean_req(requirement)}\n"
|
||||
else
|
||||
m << " #{clean_req(requirement)}\n"
|
||||
end
|
||||
m << "\n"
|
||||
end
|
||||
|
||||
def dependency_tree(m, requirements)
|
||||
requirements.each_with_index do |i, j|
|
||||
m << " " << (" " * j)
|
||||
m << "#{clean_req(i)}"
|
||||
m << " depends on\n"
|
||||
end
|
||||
m << " " << (" " * requirements.size)
|
||||
end
|
||||
|
||||
def error_message
|
||||
errors.inject("") do |o, (conflict, (origin, requirement))|
|
||||
|
||||
# origin is the SpecSet of specs from the Gemfile that is conflicted with
|
||||
if origin
|
||||
|
||||
o << %{Bundler could not find compatible versions for gem "#{origin.name}":\n}
|
||||
o << " In Gemfile:\n"
|
||||
|
||||
required_by = requirement.required_by
|
||||
o << gem_message(requirement, required_by)
|
||||
|
||||
# If the origin is "bundler", the conflict is us
|
||||
if origin.name == "bundler"
|
||||
o << " Current Bundler version:\n"
|
||||
other_bundler_required = !requirement.requirement.satisfied_by?(origin.version)
|
||||
# If the origin is a LockfileParser, it does not respond_to :required_by
|
||||
elsif !origin.respond_to?(:required_by) || !(origin.required_by.first)
|
||||
o << " In snapshot (Gemfile.lock):\n"
|
||||
end
|
||||
|
||||
required_by = origin.required_by[0..-2]
|
||||
o << gem_message(origin, required_by)
|
||||
|
||||
# If the bundle wants a newer bundler than the running bundler, explain
|
||||
if origin.name == "bundler" && other_bundler_required
|
||||
o << "This Gemfile requires a different version of Bundler.\n"
|
||||
o << "Perhaps you need to update Bundler by running `gem install bundler`?"
|
||||
end
|
||||
|
||||
# origin is nil if the required gem and version cannot be found in any of
|
||||
# the specified sources
|
||||
else
|
||||
|
||||
# if the gem cannot be found because of a version conflict between lockfile and gemfile,
|
||||
# print a useful error that suggests running `bundle update`, which may fix things
|
||||
#
|
||||
# @base is a SpecSet of the gems in the lockfile
|
||||
# conflict is the name of the gem that could not be found
|
||||
if locked = @base[conflict].first
|
||||
o << "Bundler could not find compatible versions for gem #{conflict.inspect}:\n"
|
||||
o << " In snapshot (Gemfile.lock):\n"
|
||||
o << " #{clean_req(locked)}\n\n"
|
||||
|
||||
o << " In Gemfile:\n"
|
||||
|
||||
required_by = requirement.required_by
|
||||
o << gem_message(requirement, required_by)
|
||||
o << "Running `bundle update` will rebuild your snapshot from scratch, using only\n"
|
||||
o << "the gems in your Gemfile, which may resolve the conflict.\n"
|
||||
|
||||
# the rest of the time, the gem cannot be found because it does not exist in the known sources
|
||||
else
|
||||
if requirement.required_by.first
|
||||
o << "Could not find gem '#{clean_req(requirement)}', which is required by "
|
||||
o << "gem '#{clean_req(requirement.required_by.first)}', in any of the sources."
|
||||
else
|
||||
o << "Could not find gem '#{clean_req(requirement)} in any of the sources\n"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
o
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Indicates progress by writing a '.' every iteration_rate time which is
|
||||
# approximately every second. iteration_rate is calculated in the first
|
||||
# second of resolve running.
|
||||
def indicate_progress
|
||||
@iteration_counter += 1
|
||||
|
||||
if iteration_rate.nil?
|
||||
if ((Time.now - started_at) % 3600).round >= 1
|
||||
@iteration_rate = iteration_counter
|
||||
end
|
||||
else
|
||||
if ((iteration_counter % iteration_rate) == 0)
|
||||
Bundler.ui.info ".", false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,60 @@
|
|||
module Bundler
|
||||
# General purpose class for retrying code that may fail
|
||||
class Retry
|
||||
DEFAULT_ATTEMPTS = 2
|
||||
attr_accessor :name, :total_runs, :current_run
|
||||
|
||||
class << self
|
||||
attr_accessor :attempts
|
||||
end
|
||||
|
||||
def initialize(name, exceptions = nil, attempts = nil)
|
||||
@name = name
|
||||
attempts ||= default_attempts
|
||||
@exceptions = Array(exceptions) || []
|
||||
@total_runs = attempts.next # will run once, then upto attempts.times
|
||||
end
|
||||
|
||||
def default_attempts
|
||||
return Integer(self.class.attempts) if self.class.attempts
|
||||
DEFAULT_ATTEMPTS
|
||||
end
|
||||
|
||||
def attempt(&block)
|
||||
@current_run = 0
|
||||
@failed = false
|
||||
@error = nil
|
||||
while keep_trying? do
|
||||
run(&block)
|
||||
end
|
||||
@result
|
||||
end
|
||||
alias :attempts :attempt
|
||||
|
||||
private
|
||||
def run(&block)
|
||||
@failed = false
|
||||
@current_run += 1
|
||||
@result = block.call
|
||||
rescue => e
|
||||
fail(e)
|
||||
end
|
||||
|
||||
def fail(e)
|
||||
@failed = true
|
||||
raise e if last_attempt? || @exceptions.any?{ |k| e.is_a?(k) }
|
||||
return true unless name
|
||||
Bundler.ui.warn "Retrying#{" #{name}" if name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}"
|
||||
end
|
||||
|
||||
def keep_trying?
|
||||
return true if current_run.zero?
|
||||
return false if last_attempt?
|
||||
return true if @failed
|
||||
end
|
||||
|
||||
def last_attempt?
|
||||
current_run >= total_runs
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
module Bundler
|
||||
module RubyDsl
|
||||
def ruby(ruby_version, options = {})
|
||||
raise GemfileError, "Please define :engine_version" if options[:engine] && options[:engine_version].nil?
|
||||
raise GemfileError, "Please define :engine" if options[:engine_version] && options[:engine].nil?
|
||||
|
||||
raise GemfileError, "ruby_version must match the :engine_version for MRI" if options[:engine] == "ruby" && options[:engine_version] && ruby_version != options[:engine_version]
|
||||
@ruby_version = RubyVersion.new(ruby_version, options[:patchlevel], options[:engine], options[:engine_version])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,117 @@
|
|||
module Bundler
|
||||
class RubyVersion
|
||||
attr_reader :version, :patchlevel, :engine, :engine_version
|
||||
|
||||
def initialize(version, patchlevel, engine, engine_version)
|
||||
# The parameters to this method must satisfy the
|
||||
# following constraints, which are verified in
|
||||
# the DSL:
|
||||
#
|
||||
# * If an engine is specified, an engine version
|
||||
# must also be specified
|
||||
# * If an engine version is specified, an engine
|
||||
# must also be specified
|
||||
# * If the engine is "ruby", the engine version
|
||||
# must not be specified, or the engine version
|
||||
# specified must match the version.
|
||||
|
||||
@version = version
|
||||
@engine = engine || "ruby"
|
||||
# keep track of the engine specified by the user
|
||||
@input_engine = engine
|
||||
@engine_version = engine_version || version
|
||||
@patchlevel = patchlevel
|
||||
end
|
||||
|
||||
def to_s
|
||||
output = "ruby #{version}"
|
||||
output << "p#{patchlevel}" if patchlevel
|
||||
output << " (#{engine} #{engine_version})" unless engine == "ruby"
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
version == other.version &&
|
||||
engine == other.engine &&
|
||||
engine_version == other.engine_version &&
|
||||
patchlevel == other.patchlevel
|
||||
end
|
||||
|
||||
# Returns a tuple of thsee things:
|
||||
# [diff, this, other]
|
||||
# The priority of attributes are
|
||||
# 1. engine
|
||||
# 2. ruby_version
|
||||
# 3. engine_version
|
||||
def diff(other)
|
||||
if engine != other.engine && @input_engine
|
||||
[ :engine, engine, other.engine ]
|
||||
elsif version != other.version
|
||||
[ :version, version, other.version ]
|
||||
elsif engine_version != other.engine_version && @input_engine
|
||||
[ :engine_version, engine_version, other.engine_version ]
|
||||
elsif patchlevel != other.patchlevel && @patchlevel
|
||||
[ :patchlevel, patchlevel, other.patchlevel ]
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def host
|
||||
@host ||= [
|
||||
RbConfig::CONFIG["host_cpu"],
|
||||
RbConfig::CONFIG["host_vendor"],
|
||||
RbConfig::CONFIG["host_os"]
|
||||
].join("-")
|
||||
end
|
||||
end
|
||||
|
||||
# A subclass of RubyVersion that implements version,
|
||||
# engine and engine_version based upon the current
|
||||
# information in the system. It can be used anywhere
|
||||
# a RubyVersion object is expected, and can be
|
||||
# compared with a RubyVersion object.
|
||||
class SystemRubyVersion < RubyVersion
|
||||
def initialize(*)
|
||||
# override the default initialize, because
|
||||
# we will implement version, engine and
|
||||
# engine_version dynamically
|
||||
end
|
||||
|
||||
def version
|
||||
RUBY_VERSION.dup
|
||||
end
|
||||
|
||||
def gem_version
|
||||
@gem_version ||= Gem::Version.new(version)
|
||||
end
|
||||
|
||||
def engine
|
||||
if defined?(RUBY_ENGINE)
|
||||
RUBY_ENGINE.dup
|
||||
else
|
||||
# not defined in ruby 1.8.7
|
||||
"ruby"
|
||||
end
|
||||
end
|
||||
|
||||
def engine_version
|
||||
case engine
|
||||
when "ruby"
|
||||
RUBY_VERSION.dup
|
||||
when "rbx"
|
||||
Rubinius::VERSION.dup
|
||||
when "jruby"
|
||||
JRUBY_VERSION.dup
|
||||
else
|
||||
raise BundlerError, "RUBY_ENGINE value #{RUBY_ENGINE} is not recognized"
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def patchlevel
|
||||
RUBY_PATCHLEVEL.to_s
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,167 @@
|
|||
require 'pathname'
|
||||
|
||||
if defined?(Gem::QuickLoader)
|
||||
# Gem Prelude makes me a sad panda :'(
|
||||
Gem::QuickLoader.load_full_rubygems_library
|
||||
end
|
||||
|
||||
require 'rubygems'
|
||||
require 'rubygems/specification'
|
||||
require 'bundler/match_platform'
|
||||
|
||||
module Gem
|
||||
@loaded_stacks = Hash.new { |h,k| h[k] = [] }
|
||||
|
||||
class Specification
|
||||
attr_accessor :source_uri, :location, :relative_loaded_from
|
||||
|
||||
remove_method :source if instance_methods(false).include?(:source)
|
||||
attr_accessor :source
|
||||
|
||||
alias_method :rg_full_gem_path, :full_gem_path
|
||||
alias_method :rg_loaded_from, :loaded_from
|
||||
|
||||
def full_gem_path
|
||||
source.respond_to?(:path) ?
|
||||
Pathname.new(loaded_from).dirname.expand_path(Bundler.root).to_s :
|
||||
rg_full_gem_path
|
||||
end
|
||||
|
||||
def loaded_from
|
||||
relative_loaded_from ?
|
||||
source.path.join(relative_loaded_from).to_s :
|
||||
rg_loaded_from
|
||||
end
|
||||
|
||||
def load_paths
|
||||
return full_require_paths if respond_to?(:full_require_paths)
|
||||
|
||||
require_paths.map do |require_path|
|
||||
if require_path.include?(full_gem_path)
|
||||
require_path
|
||||
else
|
||||
File.join(full_gem_path, require_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if method_defined?(:extension_dir)
|
||||
alias_method :rg_extension_dir, :extension_dir
|
||||
def extension_dir
|
||||
@extension_dir ||= source.respond_to?(:extension_dir_name) ?
|
||||
File.expand_path(File.join(extensions_dir, source.extension_dir_name)) :
|
||||
rg_extension_dir
|
||||
end
|
||||
end
|
||||
|
||||
# RubyGems 1.8+ used only.
|
||||
remove_method :gem_dir if instance_methods(false).include?(:gem_dir)
|
||||
def gem_dir
|
||||
full_gem_path
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups ||= []
|
||||
end
|
||||
|
||||
def git_version
|
||||
return unless loaded_from && source.is_a?(Bundler::Source::Git)
|
||||
" #{source.revision[0..6]}"
|
||||
end
|
||||
|
||||
def to_gemfile(path = nil)
|
||||
gemfile = "source :gemcutter\n"
|
||||
gemfile << dependencies_to_gemfile(nondevelopment_dependencies)
|
||||
unless development_dependencies.empty?
|
||||
gemfile << "\n"
|
||||
gemfile << dependencies_to_gemfile(development_dependencies, :development)
|
||||
end
|
||||
gemfile
|
||||
end
|
||||
|
||||
def nondevelopment_dependencies
|
||||
dependencies - development_dependencies
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dependencies_to_gemfile(dependencies, group = nil)
|
||||
gemfile = ''
|
||||
if dependencies.any?
|
||||
gemfile << "group :#{group} do\n" if group
|
||||
dependencies.each do |dependency|
|
||||
gemfile << ' ' if group
|
||||
gemfile << %|gem "#{dependency.name}"|
|
||||
req = dependency.requirements_list.first
|
||||
gemfile << %|, "#{req}"| if req
|
||||
gemfile << "\n"
|
||||
end
|
||||
gemfile << "end\n" if group
|
||||
end
|
||||
gemfile
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Dependency
|
||||
attr_accessor :source, :groups
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def encode_with(coder)
|
||||
to_yaml_properties.each do |ivar|
|
||||
coder[ivar.to_s.sub(/^@/, '')] = instance_variable_get(ivar)
|
||||
end
|
||||
end
|
||||
|
||||
def to_yaml_properties
|
||||
instance_variables.reject { |p| ["@source", "@groups"].include?(p.to_s) }
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = " #{name}"
|
||||
unless requirement == Gem::Requirement.default
|
||||
reqs = requirement.requirements.map{|o,v| "#{o} #{v}" }.sort.reverse
|
||||
out << " (#{reqs.join(', ')})"
|
||||
end
|
||||
out
|
||||
end
|
||||
|
||||
# Backport of performance enhancement added to Rubygems 1.4
|
||||
def matches_spec?(spec)
|
||||
# name can be a Regexp, so use ===
|
||||
return false unless name === spec.name
|
||||
return true if requirement.none?
|
||||
|
||||
requirement.satisfied_by?(spec.version)
|
||||
end unless allocate.respond_to?(:matches_spec?)
|
||||
end
|
||||
|
||||
class Requirement
|
||||
# Backport of performance enhancement added to Rubygems 1.4
|
||||
def none?
|
||||
@none ||= (to_s == ">= 0")
|
||||
end unless allocate.respond_to?(:none?)
|
||||
end
|
||||
|
||||
class Platform
|
||||
JAVA = Gem::Platform.new('java') unless defined?(JAVA)
|
||||
MSWIN = Gem::Platform.new('mswin32') unless defined?(MSWIN)
|
||||
MINGW = Gem::Platform.new('x86-mingw32') unless defined?(MINGW)
|
||||
X64_MINGW = Gem::Platform.new('x64-mingw32') unless defined?(X64_MINGW)
|
||||
|
||||
undef_method :hash if method_defined? :hash
|
||||
def hash
|
||||
@cpu.hash ^ @os.hash ^ @version.hash
|
||||
end
|
||||
|
||||
undef_method :eql? if method_defined? :eql?
|
||||
alias eql? ==
|
||||
end
|
||||
end
|
||||
|
||||
module Gem
|
||||
class Specification
|
||||
include ::Bundler::MatchPlatform
|
||||
end
|
||||
end
|
|
@ -0,0 +1,578 @@
|
|||
require 'rubygems'
|
||||
require 'rubygems/config_file'
|
||||
|
||||
module Bundler
|
||||
class RubygemsIntegration
|
||||
|
||||
def self.version
|
||||
@version ||= Gem::Version.new(Gem::VERSION)
|
||||
end
|
||||
|
||||
def self.provides?(req_str)
|
||||
Gem::Requirement.new(req_str).satisfied_by?(version)
|
||||
end
|
||||
|
||||
def version
|
||||
self.class.version
|
||||
end
|
||||
|
||||
def provides?(req_str)
|
||||
self.class.provides?(req_str)
|
||||
end
|
||||
|
||||
def build_args
|
||||
Gem::Command.build_args
|
||||
end
|
||||
|
||||
def build_args=(args)
|
||||
Gem::Command.build_args = args
|
||||
end
|
||||
|
||||
def loaded_specs(name)
|
||||
Gem.loaded_specs[name]
|
||||
end
|
||||
|
||||
def mark_loaded(spec)
|
||||
Gem.loaded_specs[spec.name] = spec
|
||||
end
|
||||
|
||||
def path(obj)
|
||||
obj.to_s
|
||||
end
|
||||
|
||||
def platforms
|
||||
Gem.platforms
|
||||
end
|
||||
|
||||
def configuration
|
||||
Gem.configuration
|
||||
rescue Gem::SystemExitException => e
|
||||
Bundler.ui.error "#{e.class}: #{e.message}"
|
||||
Bundler.ui.trace e
|
||||
raise Gem::SystemExitException
|
||||
end
|
||||
|
||||
def ruby_engine
|
||||
Gem.ruby_engine
|
||||
end
|
||||
|
||||
def read_binary(path)
|
||||
Gem.read_binary(path)
|
||||
end
|
||||
|
||||
def inflate(obj)
|
||||
Gem.inflate(obj)
|
||||
end
|
||||
|
||||
def sources=(val)
|
||||
# Gem.configuration creates a new Gem::ConfigFile, which by default will read ~/.gemrc
|
||||
# If that file exists, its settings (including sources) will overwrite the values we
|
||||
# are about to set here. In order to avoid that, we force memoizing the config file now.
|
||||
configuration
|
||||
|
||||
Gem.sources = val
|
||||
end
|
||||
|
||||
def sources
|
||||
Gem.sources
|
||||
end
|
||||
|
||||
def gem_dir
|
||||
Gem.dir
|
||||
end
|
||||
|
||||
def gem_bindir
|
||||
Gem.bindir
|
||||
end
|
||||
|
||||
def user_home
|
||||
Gem.user_home
|
||||
end
|
||||
|
||||
def gem_path
|
||||
Gem.path
|
||||
end
|
||||
|
||||
def gem_cache
|
||||
gem_path.map{|p| File.expand_path("cache", p) }
|
||||
end
|
||||
|
||||
def spec_cache_dirs
|
||||
@spec_cache_dirs ||= begin
|
||||
dirs = gem_path.map {|dir| File.join(dir, 'specifications')}
|
||||
dirs << Gem.spec_cache_dir if Gem.respond_to?(:spec_cache_dir) # Not in Rubygems 2.0.3 or earlier
|
||||
dirs.uniq.select {|dir| File.directory? dir}
|
||||
end
|
||||
end
|
||||
|
||||
def marshal_spec_dir
|
||||
Gem::MARSHAL_SPEC_DIR
|
||||
end
|
||||
|
||||
def config_map
|
||||
Gem::ConfigMap
|
||||
end
|
||||
|
||||
def repository_subdirectories
|
||||
%w[cache doc gems specifications]
|
||||
end
|
||||
|
||||
def clear_paths
|
||||
Gem.clear_paths
|
||||
end
|
||||
|
||||
def bin_path(gem, bin, ver)
|
||||
Gem.bin_path(gem, bin, ver)
|
||||
end
|
||||
|
||||
def preserve_paths
|
||||
# this is a no-op outside of Rubygems 1.8
|
||||
yield
|
||||
end
|
||||
|
||||
def ui=(obj)
|
||||
Gem::DefaultUserInteraction.ui = obj
|
||||
end
|
||||
|
||||
def fetch_specs(all, pre, &blk)
|
||||
specs = Gem::SpecFetcher.new.list(all, pre)
|
||||
specs.each { yield } if block_given?
|
||||
specs
|
||||
end
|
||||
|
||||
def fetch_prerelease_specs
|
||||
fetch_specs(false, true)
|
||||
rescue Gem::RemoteFetcher::FetchError
|
||||
[] # if we can't download them, there aren't any
|
||||
end
|
||||
|
||||
def fetch_all_remote_specs
|
||||
# Fetch all specs, minus prerelease specs
|
||||
spec_list = fetch_specs(true, false)
|
||||
# Then fetch the prerelease specs
|
||||
fetch_prerelease_specs.each {|k, v| spec_list[k] += v }
|
||||
|
||||
return spec_list
|
||||
end
|
||||
|
||||
def with_build_args(args)
|
||||
old_args = self.build_args
|
||||
begin
|
||||
self.build_args = args
|
||||
yield
|
||||
ensure
|
||||
self.build_args = old_args
|
||||
end
|
||||
end
|
||||
|
||||
def gem_from_path(path, policy = nil)
|
||||
require 'rubygems/format'
|
||||
Gem::Format.from_file_by_path(path, policy)
|
||||
end
|
||||
|
||||
def spec_from_gem(path, policy = nil)
|
||||
require 'rubygems/security'
|
||||
gem_from_path(path, security_policies[policy]).spec
|
||||
rescue Gem::Package::FormatError
|
||||
raise GemspecError, "Could not read gem at #{path}. It may be corrupted."
|
||||
rescue Exception, Gem::Exception, Gem::Security::Exception => e
|
||||
if e.is_a?(Gem::Security::Exception) ||
|
||||
e.message =~ /unknown trust policy|unsigned gem/i ||
|
||||
e.message =~ /couldn't verify (meta)?data signature/i
|
||||
raise SecurityError,
|
||||
"The gem #{File.basename(path, '.gem')} can't be installed because " \
|
||||
"the security policy didn't allow it, with the message: #{e.message}"
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
def build(spec, skip_validation = false)
|
||||
require 'rubygems/builder'
|
||||
Gem::Builder.new(spec).build
|
||||
end
|
||||
|
||||
def build_gem(gem_dir, spec)
|
||||
SharedHelpers.chdir(gem_dir) { build(spec) }
|
||||
end
|
||||
|
||||
def download_gem(spec, uri, path)
|
||||
uri = Bundler::Source.mirror_for(uri)
|
||||
fetcher = Gem::RemoteFetcher.new(configuration[:http_proxy])
|
||||
fetcher.download(spec, uri, path)
|
||||
end
|
||||
|
||||
def security_policy_keys
|
||||
%w{High Medium Low AlmostNo No}.map { |level| "#{level}Security" }
|
||||
end
|
||||
|
||||
def security_policies
|
||||
@security_policies ||= begin
|
||||
require 'rubygems/security'
|
||||
Gem::Security::Policies
|
||||
rescue LoadError, NameError
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
def reverse_rubygems_kernel_mixin
|
||||
# Disable rubygems' gem activation system
|
||||
::Kernel.class_eval do
|
||||
if private_method_defined?(:gem_original_require)
|
||||
alias rubygems_require require
|
||||
alias require gem_original_require
|
||||
end
|
||||
|
||||
undef gem
|
||||
end
|
||||
end
|
||||
|
||||
def replace_gem(specs)
|
||||
reverse_rubygems_kernel_mixin
|
||||
|
||||
executables = specs.map { |s| s.executables }.flatten
|
||||
|
||||
::Kernel.send(:define_method, :gem) do |dep, *reqs|
|
||||
if executables.include? File.basename(caller.first.split(':').first)
|
||||
return
|
||||
end
|
||||
reqs.pop if reqs.last.is_a?(Hash)
|
||||
|
||||
unless dep.respond_to?(:name) && dep.respond_to?(:requirement)
|
||||
dep = Gem::Dependency.new(dep, reqs)
|
||||
end
|
||||
|
||||
spec = specs.find { |s| s.name == dep.name }
|
||||
|
||||
if spec.nil?
|
||||
|
||||
e = Gem::LoadError.new "#{dep.name} is not part of the bundle. Add it to Gemfile."
|
||||
e.name = dep.name
|
||||
if e.respond_to?(:requirement=)
|
||||
e.requirement = dep.requirement
|
||||
else
|
||||
e.version_requirement = dep.requirement
|
||||
end
|
||||
raise e
|
||||
elsif dep !~ spec
|
||||
e = Gem::LoadError.new "can't activate #{dep}, already activated #{spec.full_name}. " \
|
||||
"Make sure all dependencies are added to Gemfile."
|
||||
e.name = dep.name
|
||||
if e.respond_to?(:requirement=)
|
||||
e.requirement = dep.requirement
|
||||
else
|
||||
e.version_requirement = dep.requirement
|
||||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def stub_source_index(specs)
|
||||
Gem::SourceIndex.send(:alias_method, :old_initialize, :initialize)
|
||||
redefine_method(Gem::SourceIndex, :initialize) do |*args|
|
||||
@gems = {}
|
||||
# You're looking at this thinking: Oh! This is how I make those
|
||||
# rubygems deprecations go away!
|
||||
#
|
||||
# You'd be correct BUT using of this method in production code
|
||||
# must be approved by the rubygems team itself!
|
||||
#
|
||||
# This is your warning. If you use this and don't have approval
|
||||
# we can't protect you.
|
||||
#
|
||||
Deprecate.skip_during do
|
||||
self.spec_dirs = *args
|
||||
add_specs(*specs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Used to make bin stubs that are not created by bundler work
|
||||
# under bundler. The new Gem.bin_path only considers gems in
|
||||
# +specs+
|
||||
def replace_bin_path(specs)
|
||||
gem_class = (class << Gem ; self ; end)
|
||||
redefine_method(gem_class, :bin_path) do |name, *args|
|
||||
exec_name = args.first
|
||||
|
||||
if exec_name == 'bundle'
|
||||
return ENV['BUNDLE_BIN_PATH']
|
||||
end
|
||||
|
||||
spec = nil
|
||||
|
||||
if exec_name
|
||||
spec = specs.find { |s| s.executables.include?(exec_name) }
|
||||
unless spec.name == name
|
||||
warn "Bundler is using a binstub that was created for a different gem.\n" \
|
||||
"This is deprecated, in future versions you may need to `bundle binstub #{name}` " \
|
||||
"to work around a system/bundle conflict."
|
||||
end
|
||||
spec or raise Gem::Exception, "can't find executable #{exec_name}"
|
||||
else
|
||||
spec = specs.find { |s| s.name == name }
|
||||
exec_name = spec.default_executable or raise Gem::Exception, "no default executable for #{spec.full_name}"
|
||||
end
|
||||
|
||||
gem_bin = File.join(spec.full_gem_path, spec.bindir, exec_name)
|
||||
gem_from_path_bin = File.join(File.dirname(spec.loaded_from), spec.bindir, exec_name)
|
||||
File.exist?(gem_bin) ? gem_bin : gem_from_path_bin
|
||||
end
|
||||
end
|
||||
|
||||
# Because Bundler has a static view of what specs are available,
|
||||
# we don't #refresh, so stub it out.
|
||||
def replace_refresh
|
||||
gem_class = (class << Gem ; self ; end)
|
||||
redefine_method(gem_class, :refresh) { }
|
||||
end
|
||||
|
||||
# Replace or hook into Rubygems to provide a bundlerized view
|
||||
# of the world.
|
||||
def replace_entrypoints(specs)
|
||||
replace_gem(specs)
|
||||
|
||||
stub_rubygems(specs)
|
||||
|
||||
replace_bin_path(specs)
|
||||
replace_refresh
|
||||
|
||||
Gem.clear_paths
|
||||
end
|
||||
|
||||
# This backports the correct segment generation code from Rubygems 1.4+
|
||||
# by monkeypatching it into the method in Rubygems 1.3.6 and 1.3.7.
|
||||
def backport_segment_generation
|
||||
redefine_method(Gem::Version, :segments) do
|
||||
@segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
|
||||
/^\d+$/ =~ s ? s.to_i : s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This backport fixes the marshaling of @segments.
|
||||
def backport_yaml_initialize
|
||||
redefine_method(Gem::Version, :yaml_initialize) do |tag, map|
|
||||
@version = map['version']
|
||||
@segments = nil
|
||||
@hash = nil
|
||||
end
|
||||
end
|
||||
|
||||
# This backports base_dir which replaces installation path
|
||||
# Rubygems 1.8+
|
||||
def backport_base_dir
|
||||
redefine_method(Gem::Specification, :base_dir) do
|
||||
return Gem.dir unless loaded_from
|
||||
File.dirname File.dirname loaded_from
|
||||
end
|
||||
end
|
||||
|
||||
def backport_cache_file
|
||||
redefine_method(Gem::Specification, :cache_dir) do
|
||||
@cache_dir ||= File.join base_dir, "cache"
|
||||
end
|
||||
|
||||
redefine_method(Gem::Specification, :cache_file) do
|
||||
@cache_file ||= File.join cache_dir, "#{full_name}.gem"
|
||||
end
|
||||
end
|
||||
|
||||
def backport_spec_file
|
||||
redefine_method(Gem::Specification, :spec_dir) do
|
||||
@spec_dir ||= File.join base_dir, "specifications"
|
||||
end
|
||||
|
||||
redefine_method(Gem::Specification, :spec_file) do
|
||||
@spec_file ||= File.join spec_dir, "#{full_name}.gemspec"
|
||||
end
|
||||
end
|
||||
|
||||
def redefine_method(klass, method, &block)
|
||||
if klass.instance_methods(false).include?(method)
|
||||
klass.send(:remove_method, method)
|
||||
end
|
||||
klass.send(:define_method, method, &block)
|
||||
end
|
||||
|
||||
# Rubygems 1.4 through 1.6
|
||||
class Legacy < RubygemsIntegration
|
||||
def initialize
|
||||
super
|
||||
backport_base_dir
|
||||
backport_cache_file
|
||||
backport_spec_file
|
||||
backport_yaml_initialize
|
||||
end
|
||||
|
||||
def stub_rubygems(specs)
|
||||
# Rubygems versions lower than 1.7 use SourceIndex#from_gems_in
|
||||
source_index_class = (class << Gem::SourceIndex ; self ; end)
|
||||
source_index_class.send(:define_method, :from_gems_in) do |*args|
|
||||
source_index = Gem::SourceIndex.new
|
||||
source_index.spec_dirs = *args
|
||||
source_index.add_specs(*specs)
|
||||
source_index
|
||||
end
|
||||
end
|
||||
|
||||
def all_specs
|
||||
Gem.source_index.gems.values
|
||||
end
|
||||
|
||||
def find_name(name)
|
||||
Gem.source_index.find_name(name)
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems versions 1.3.6 and 1.3.7
|
||||
class Ancient < Legacy
|
||||
def initialize
|
||||
super
|
||||
backport_segment_generation
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems 1.7
|
||||
class Transitional < Legacy
|
||||
def stub_rubygems(specs)
|
||||
stub_source_index(specs)
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems 1.8.5-1.8.19
|
||||
class Modern < RubygemsIntegration
|
||||
def stub_rubygems(specs)
|
||||
Gem::Specification.all = specs
|
||||
|
||||
Gem.post_reset {
|
||||
Gem::Specification.all = specs
|
||||
}
|
||||
|
||||
stub_source_index(specs)
|
||||
end
|
||||
|
||||
def all_specs
|
||||
Gem::Specification.to_a
|
||||
end
|
||||
|
||||
def find_name(name)
|
||||
Gem::Specification.find_all_by_name name
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems 1.8.0 to 1.8.4
|
||||
class AlmostModern < Modern
|
||||
# Rubygems [>= 1.8.0, < 1.8.5] has a bug that changes Gem.dir whenever
|
||||
# you call Gem::Installer#install with an :install_dir set. We have to
|
||||
# change it back for our sudo mode to work.
|
||||
def preserve_paths
|
||||
old_dir, old_path = gem_dir, gem_path
|
||||
yield
|
||||
Gem.use_paths(old_dir, old_path)
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems 1.8.20+
|
||||
class MoreModern < Modern
|
||||
# Rubygems 1.8.20 and adds the skip_validation parameter, so that's
|
||||
# when we start passing it through.
|
||||
def build(spec, skip_validation = false)
|
||||
require 'rubygems/builder'
|
||||
Gem::Builder.new(spec).build(skip_validation)
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems 2.0
|
||||
class Future < RubygemsIntegration
|
||||
def stub_rubygems(specs)
|
||||
Gem::Specification.all = specs
|
||||
|
||||
Gem.post_reset do
|
||||
Gem::Specification.all = specs
|
||||
end
|
||||
end
|
||||
|
||||
def all_specs
|
||||
Gem::Specification.to_a
|
||||
end
|
||||
|
||||
def find_name(name)
|
||||
Gem::Specification.find_all_by_name name
|
||||
end
|
||||
|
||||
def fetch_specs(source, name)
|
||||
path = source + "#{name}.#{Gem.marshal_version}.gz"
|
||||
string = Gem::RemoteFetcher.fetcher.fetch_path(path)
|
||||
Bundler.load_marshal(string)
|
||||
rescue Gem::RemoteFetcher::FetchError => e
|
||||
# it's okay for prerelease to fail
|
||||
raise e unless name == "prerelease_specs"
|
||||
end
|
||||
|
||||
def fetch_all_remote_specs
|
||||
# Since SpecFetcher now returns NameTuples, we just fetch directly
|
||||
# and unmarshal the array ourselves.
|
||||
hash = {}
|
||||
|
||||
Gem.sources.each do |source|
|
||||
source = URI.parse(source.to_s) unless source.is_a?(URI)
|
||||
hash[source] = fetch_specs(source, "specs")
|
||||
|
||||
pres = fetch_specs(source, "prerelease_specs")
|
||||
hash[source].push(*pres) if pres && !pres.empty?
|
||||
end
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
def download_gem(spec, uri, path)
|
||||
require 'resolv'
|
||||
uri = Bundler::Source.mirror_for(uri)
|
||||
proxy, dns = configuration[:http_proxy], Resolv::DNS.new
|
||||
fetcher = Gem::RemoteFetcher.new(proxy, dns)
|
||||
fetcher.download(spec, uri, path)
|
||||
end
|
||||
|
||||
def gem_from_path(path, policy = nil)
|
||||
require 'rubygems/package'
|
||||
p = Gem::Package.new(path)
|
||||
p.security_policy = policy if policy
|
||||
return p
|
||||
end
|
||||
|
||||
def build(spec, skip_validation = false)
|
||||
require 'rubygems/package'
|
||||
Gem::Package.build(spec, skip_validation)
|
||||
end
|
||||
|
||||
def repository_subdirectories
|
||||
Gem::REPOSITORY_SUBDIRECTORIES
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if RubygemsIntegration.provides?(">= 1.99.99")
|
||||
@rubygems = RubygemsIntegration::Future.new
|
||||
elsif RubygemsIntegration.provides?('>= 1.8.20')
|
||||
@rubygems = RubygemsIntegration::MoreModern.new
|
||||
elsif RubygemsIntegration.provides?('>= 1.8.5')
|
||||
@rubygems = RubygemsIntegration::Modern.new
|
||||
elsif RubygemsIntegration.provides?('>= 1.8.0')
|
||||
@rubygems = RubygemsIntegration::AlmostModern.new
|
||||
elsif RubygemsIntegration.provides?('>= 1.7.0')
|
||||
@rubygems = RubygemsIntegration::Transitional.new
|
||||
elsif RubygemsIntegration.provides?('>= 1.4.0')
|
||||
@rubygems = RubygemsIntegration::Legacy.new
|
||||
else # Rubygems 1.3.6 and 1.3.7
|
||||
@rubygems = RubygemsIntegration::Ancient.new
|
||||
end
|
||||
|
||||
class << self
|
||||
attr_reader :rubygems
|
||||
end
|
||||
end
|
|
@ -0,0 +1,310 @@
|
|||
require "digest/sha1"
|
||||
|
||||
module Bundler
|
||||
class Runtime < Environment
|
||||
include SharedHelpers
|
||||
|
||||
def setup(*groups)
|
||||
groups.map! { |g| g.to_sym }
|
||||
|
||||
# Has to happen first
|
||||
clean_load_path
|
||||
|
||||
specs = groups.any? ? @definition.specs_for(groups) : requested_specs
|
||||
|
||||
setup_environment
|
||||
Bundler.rubygems.replace_entrypoints(specs)
|
||||
|
||||
# Activate the specs
|
||||
specs.each do |spec|
|
||||
unless spec.loaded_from
|
||||
raise GemNotFound, "#{spec.full_name} is missing. Run `bundle` to get it."
|
||||
end
|
||||
|
||||
if activated_spec = Bundler.rubygems.loaded_specs(spec.name) and activated_spec.version != spec.version
|
||||
e = Gem::LoadError.new "You have already activated #{activated_spec.name} #{activated_spec.version}, " \
|
||||
"but your Gemfile requires #{spec.name} #{spec.version}. Prepending " \
|
||||
"`bundle exec` to your command may solve this."
|
||||
e.name = spec.name
|
||||
if e.respond_to?(:requirement=)
|
||||
e.requirement = Gem::Requirement.new(spec.version.to_s)
|
||||
else
|
||||
e.version_requirement = Gem::Requirement.new(spec.version.to_s)
|
||||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
Bundler.rubygems.mark_loaded(spec)
|
||||
load_paths = spec.load_paths.reject {|path| $LOAD_PATH.include?(path)}
|
||||
$LOAD_PATH.unshift(*load_paths)
|
||||
end
|
||||
|
||||
setup_manpath
|
||||
|
||||
lock
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
REGEXPS = [
|
||||
/^no such file to load -- (.+)$/i,
|
||||
/^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
|
||||
/^Missing API definition file in (.+)$/i,
|
||||
/^cannot load such file -- (.+)$/i,
|
||||
/^dlopen\([^)]*\): Library not loaded: (.+)$/i,
|
||||
]
|
||||
|
||||
def require(*groups)
|
||||
groups.map! { |g| g.to_sym }
|
||||
groups = [:default] if groups.empty?
|
||||
|
||||
@definition.dependencies.each do |dep|
|
||||
# Skip the dependency if it is not in any of the requested
|
||||
# groups
|
||||
next unless ((dep.groups & groups).any? && dep.current_platform?)
|
||||
|
||||
required_file = nil
|
||||
|
||||
begin
|
||||
# Loop through all the specified autorequires for the
|
||||
# dependency. If there are none, use the dependency's name
|
||||
# as the autorequire.
|
||||
Array(dep.autorequire || dep.name).each do |file|
|
||||
# Allow `require: true` as an alias for `require: <name>`
|
||||
file = dep.name if file == true
|
||||
required_file = file
|
||||
Kernel.require file
|
||||
end
|
||||
rescue LoadError => e
|
||||
REGEXPS.find { |r| r =~ e.message }
|
||||
raise if dep.autorequire || $1 != required_file
|
||||
|
||||
if dep.autorequire.nil? && dep.name.include?('-')
|
||||
begin
|
||||
namespaced_file = dep.name.gsub('-', '/')
|
||||
Kernel.require namespaced_file
|
||||
rescue LoadError
|
||||
REGEXPS.find { |r| r =~ e.message }
|
||||
regex_name = $1
|
||||
raise e if dep.autorequire || (regex_name && regex_name.gsub('-', '/') != namespaced_file)
|
||||
raise e if regex_name.nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def dependencies_for(*groups)
|
||||
if groups.empty?
|
||||
dependencies
|
||||
else
|
||||
dependencies.select { |d| (groups & d.groups).any? }
|
||||
end
|
||||
end
|
||||
|
||||
alias gems specs
|
||||
|
||||
def cache(custom_path = nil)
|
||||
cache_path = cache_path(custom_path)
|
||||
FileUtils.mkdir_p(cache_path) unless File.exist?(cache_path)
|
||||
|
||||
Bundler.ui.info "Updating files in vendor/cache"
|
||||
specs.each do |spec|
|
||||
next if spec.name == 'bundler'
|
||||
spec.source.cache(spec, custom_path) if spec.source.respond_to?(:cache)
|
||||
end
|
||||
|
||||
Dir[cache_path.join("*/.git")].each do |git_dir|
|
||||
FileUtils.rm_rf(git_dir)
|
||||
FileUtils.touch(File.expand_path("../.bundlecache", git_dir))
|
||||
end
|
||||
|
||||
prune_cache(custom_path) unless Bundler.settings[:no_prune]
|
||||
end
|
||||
|
||||
def prune_cache(custom_path)
|
||||
cache_path = cache_path(custom_path)
|
||||
FileUtils.mkdir_p(cache_path) unless File.exist?(cache_path)
|
||||
resolve = @definition.resolve
|
||||
prune_gem_cache(resolve, custom_path)
|
||||
prune_git_and_path_cache(resolve, custom_path)
|
||||
end
|
||||
|
||||
def clean(dry_run = false)
|
||||
gem_bins = Dir["#{Gem.dir}/bin/*"]
|
||||
git_dirs = Dir["#{Gem.dir}/bundler/gems/*"]
|
||||
git_cache_dirs = Dir["#{Gem.dir}/cache/bundler/git/*"]
|
||||
gem_dirs = Dir["#{Gem.dir}/gems/*"]
|
||||
gem_files = Dir["#{Gem.dir}/cache/*.gem"]
|
||||
gemspec_files = Dir["#{Gem.dir}/specifications/*.gemspec"]
|
||||
spec_gem_paths = []
|
||||
# need to keep git sources around
|
||||
spec_git_paths = @definition.spec_git_paths
|
||||
spec_git_cache_dirs = []
|
||||
spec_gem_executables = []
|
||||
spec_cache_paths = []
|
||||
spec_gemspec_paths = []
|
||||
specs.each do |spec|
|
||||
spec_gem_paths << spec.full_gem_path
|
||||
# need to check here in case gems are nested like for the rails git repo
|
||||
md = %r{(.+bundler/gems/.+-[a-f0-9]{7,12})}.match(spec.full_gem_path)
|
||||
spec_git_paths << md[1] if md
|
||||
spec_gem_executables << spec.executables.collect do |executable|
|
||||
e = "#{Bundler.rubygems.gem_bindir}/#{executable}"
|
||||
[e, "#{e}.bat"]
|
||||
end
|
||||
spec_cache_paths << spec.cache_file
|
||||
spec_gemspec_paths << spec.spec_file
|
||||
spec_git_cache_dirs << spec.source.cache_path.to_s if spec.source.is_a?(Bundler::Source::Git)
|
||||
end
|
||||
spec_gem_paths.uniq!
|
||||
spec_gem_executables.flatten!
|
||||
|
||||
stale_gem_bins = gem_bins - spec_gem_executables
|
||||
stale_git_dirs = git_dirs - spec_git_paths
|
||||
stale_git_cache_dirs = git_cache_dirs - spec_git_cache_dirs
|
||||
stale_gem_dirs = gem_dirs - spec_gem_paths
|
||||
stale_gem_files = gem_files - spec_cache_paths
|
||||
stale_gemspec_files = gemspec_files - spec_gemspec_paths
|
||||
|
||||
output = stale_gem_dirs.collect do |gem_dir|
|
||||
full_name = Pathname.new(gem_dir).basename.to_s
|
||||
|
||||
parts = full_name.split('-')
|
||||
name = parts[0..-2].join('-')
|
||||
version = parts.last
|
||||
output = "#{name} (#{version})"
|
||||
|
||||
if dry_run
|
||||
Bundler.ui.info "Would have removed #{output}"
|
||||
else
|
||||
Bundler.ui.info "Removing #{output}"
|
||||
FileUtils.rm_rf(gem_dir)
|
||||
end
|
||||
|
||||
output
|
||||
end + stale_git_dirs.collect do |gem_dir|
|
||||
full_name = Pathname.new(gem_dir).basename.to_s
|
||||
|
||||
parts = full_name.split('-')
|
||||
name = parts[0..-2].join('-')
|
||||
revision = parts[-1]
|
||||
output = "#{name} (#{revision})"
|
||||
|
||||
if dry_run
|
||||
Bundler.ui.info "Would have removed #{output}"
|
||||
else
|
||||
Bundler.ui.info "Removing #{output}"
|
||||
FileUtils.rm_rf(gem_dir)
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
unless dry_run
|
||||
stale_gem_bins.each { |bin| FileUtils.rm(bin) if File.exist?(bin) }
|
||||
stale_gem_files.each { |file| FileUtils.rm(file) if File.exist?(file) }
|
||||
stale_gemspec_files.each { |file| FileUtils.rm(file) if File.exist?(file) }
|
||||
stale_git_cache_dirs.each { |dir| FileUtils.rm_rf(dir) if File.exist?(dir) }
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def setup_environment
|
||||
begin
|
||||
ENV["BUNDLE_BIN_PATH"] = Bundler.rubygems.bin_path("bundler", "bundle", VERSION)
|
||||
rescue Gem::GemNotFoundException
|
||||
ENV["BUNDLE_BIN_PATH"] = File.expand_path("../../../bin/bundle", __FILE__)
|
||||
end
|
||||
|
||||
# Set PATH
|
||||
paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
|
||||
paths.unshift "#{Bundler.bundle_path}/bin"
|
||||
ENV["PATH"] = paths.uniq.join(File::PATH_SEPARATOR)
|
||||
|
||||
# Set BUNDLE_GEMFILE
|
||||
ENV["BUNDLE_GEMFILE"] = default_gemfile.to_s
|
||||
|
||||
# Set RUBYOPT
|
||||
rubyopt = [ENV["RUBYOPT"]].compact
|
||||
if rubyopt.empty? || rubyopt.first !~ /-rbundler\/setup/
|
||||
rubyopt.unshift %|-rbundler/setup|
|
||||
ENV["RUBYOPT"] = rubyopt.join(' ')
|
||||
end
|
||||
|
||||
# Set RUBYLIB
|
||||
rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR)
|
||||
rubylib.unshift File.expand_path('../..', __FILE__)
|
||||
ENV["RUBYLIB"] = rubylib.uniq.join(File::PATH_SEPARATOR)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prune_gem_cache(resolve, custom_path)
|
||||
cached = Dir["#{cache_path(custom_path)}/*.gem"]
|
||||
|
||||
cached = cached.delete_if do |path|
|
||||
spec = Bundler.rubygems.spec_from_gem path
|
||||
|
||||
resolve.any? do |s|
|
||||
s.name == spec.name && s.version == spec.version && !s.source.is_a?(Bundler::Source::Git)
|
||||
end
|
||||
end
|
||||
|
||||
if cached.any?
|
||||
Bundler.ui.info "Removing outdated .gem files from vendor/cache"
|
||||
|
||||
cached.each do |path|
|
||||
Bundler.ui.info " * #{File.basename(path)}"
|
||||
File.delete(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def prune_git_and_path_cache(resolve, custom_path)
|
||||
cached = Dir["#{cache_path(custom_path)}/*/.bundlecache"]
|
||||
|
||||
cached = cached.delete_if do |path|
|
||||
name = File.basename(File.dirname(path))
|
||||
|
||||
resolve.any? do |s|
|
||||
source = s.source
|
||||
source.respond_to?(:app_cache_dirname) && source.app_cache_dirname == name
|
||||
end
|
||||
end
|
||||
|
||||
if cached.any?
|
||||
Bundler.ui.info "Removing outdated git and path gems from vendor/cache"
|
||||
|
||||
cached.each do |path|
|
||||
path = File.dirname(path)
|
||||
Bundler.ui.info " * #{File.basename(path)}"
|
||||
FileUtils.rm_rf(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setup_manpath
|
||||
# Store original MANPATH for restoration later in with_clean_env()
|
||||
ENV['BUNDLE_ORIG_MANPATH'] = ENV['MANPATH']
|
||||
|
||||
# Add man/ subdirectories from activated bundles to MANPATH for man(1)
|
||||
manuals = $LOAD_PATH.map do |path|
|
||||
man_subdir = path.sub(/lib$/, 'man')
|
||||
man_subdir unless Dir[man_subdir + '/man?/'].empty?
|
||||
end.compact
|
||||
|
||||
unless manuals.empty?
|
||||
ENV['MANPATH'] = manuals.concat(
|
||||
ENV['MANPATH'].to_s.split(File::PATH_SEPARATOR)
|
||||
).uniq.join(File::PATH_SEPARATOR)
|
||||
end
|
||||
end
|
||||
|
||||
def cache_path(custom_path = nil)
|
||||
path = custom_path || root
|
||||
path.join("vendor/cache")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,176 @@
|
|||
module Bundler
|
||||
class Settings
|
||||
def initialize(root = nil)
|
||||
@root = root
|
||||
@local_config = load_config(local_config_file)
|
||||
@global_config = load_config(global_config_file)
|
||||
end
|
||||
|
||||
def [](key)
|
||||
the_key = key_for(key)
|
||||
value = (@local_config[the_key] || ENV[the_key] || @global_config[the_key])
|
||||
is_bool(key) ? to_bool(value) : value
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
local_config_file or raise GemfileNotFound, "Could not locate Gemfile"
|
||||
set_key(key, value, @local_config, local_config_file)
|
||||
end
|
||||
|
||||
alias :set_local :[]=
|
||||
|
||||
def delete(key)
|
||||
@local_config.delete(key_for(key))
|
||||
end
|
||||
|
||||
def set_global(key, value)
|
||||
set_key(key, value, @global_config, global_config_file)
|
||||
end
|
||||
|
||||
def all
|
||||
env_keys = ENV.keys.select { |k| k =~ /BUNDLE_.*/ }
|
||||
|
||||
keys = @global_config.keys | @local_config.keys | env_keys
|
||||
|
||||
keys.map do |key|
|
||||
key.sub(/^BUNDLE_/, '').gsub(/__/, ".").downcase
|
||||
end
|
||||
end
|
||||
|
||||
def local_overrides
|
||||
repos = {}
|
||||
all.each do |k|
|
||||
if k =~ /^local\./
|
||||
repos[$'] = self[k]
|
||||
end
|
||||
end
|
||||
repos
|
||||
end
|
||||
|
||||
def gem_mirrors
|
||||
all.inject({}) do |h, k|
|
||||
if k =~ /^mirror\./
|
||||
uri = normalize_uri($')
|
||||
h[uri] = normalize_uri(self[k])
|
||||
end
|
||||
h
|
||||
end
|
||||
end
|
||||
|
||||
def locations(key)
|
||||
key = key_for(key)
|
||||
locations = {}
|
||||
locations[:local] = @local_config[key] if @local_config.key?(key)
|
||||
locations[:env] = ENV[key] if ENV[key]
|
||||
locations[:global] = @global_config[key] if @global_config.key?(key)
|
||||
locations
|
||||
end
|
||||
|
||||
def pretty_values_for(exposed_key)
|
||||
key = key_for(exposed_key)
|
||||
|
||||
locations = []
|
||||
if @local_config.key?(key)
|
||||
locations << "Set for your local app (#{local_config_file}): #{@local_config[key].inspect}"
|
||||
end
|
||||
|
||||
if value = ENV[key]
|
||||
locations << "Set via #{key}: #{value.inspect}"
|
||||
end
|
||||
|
||||
if @global_config.key?(key)
|
||||
locations << "Set for the current user (#{global_config_file}): #{@global_config[key].inspect}"
|
||||
end
|
||||
|
||||
return ["You have not configured a value for `#{exposed_key}`"] if locations.empty?
|
||||
locations
|
||||
end
|
||||
|
||||
def without=(array)
|
||||
self[:without] = (array.empty? ? nil : array.join(":")) if array
|
||||
end
|
||||
|
||||
def without
|
||||
self[:without] ? self[:without].split(":").map { |w| w.to_sym } : []
|
||||
end
|
||||
|
||||
# @local_config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"]
|
||||
def path
|
||||
key = key_for(:path)
|
||||
path = ENV[key] || @global_config[key]
|
||||
return path if path && !@local_config.key?(key)
|
||||
|
||||
if path = self[:path]
|
||||
"#{path}/#{Bundler.ruby_scope}"
|
||||
else
|
||||
Bundler.rubygems.gem_dir
|
||||
end
|
||||
end
|
||||
|
||||
def allow_sudo?
|
||||
!@local_config.key?(key_for(:path))
|
||||
end
|
||||
|
||||
def ignore_config?
|
||||
ENV['BUNDLE_IGNORE_CONFIG']
|
||||
end
|
||||
|
||||
private
|
||||
def key_for(key)
|
||||
key = key.to_s.sub(".", "__").upcase
|
||||
"BUNDLE_#{key}"
|
||||
end
|
||||
|
||||
def is_bool(key)
|
||||
%w(frozen cache_all no_prune disable_local_branch_check).include? key.to_s
|
||||
end
|
||||
|
||||
def to_bool(value)
|
||||
!(value.nil? || value == '' || value =~ /^(false|f|no|n|0)$/i)
|
||||
end
|
||||
|
||||
def set_key(key, value, hash, file)
|
||||
key = key_for(key)
|
||||
|
||||
unless hash[key] == value
|
||||
hash[key] = value
|
||||
hash.delete(key) if value.nil?
|
||||
FileUtils.mkdir_p(file.dirname)
|
||||
require 'bundler/psyched_yaml'
|
||||
File.open(file, "w") { |f| f.puts YAML.dump(hash) }
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
def global_config_file
|
||||
file = ENV["BUNDLE_CONFIG"] || File.join(Bundler.rubygems.user_home, ".bundle/config")
|
||||
Pathname.new(file)
|
||||
end
|
||||
|
||||
def local_config_file
|
||||
Pathname.new(@root).join("config") if @root
|
||||
end
|
||||
|
||||
def load_config(config_file)
|
||||
valid_file = config_file && config_file.exist? && !config_file.size.zero?
|
||||
if !ignore_config? && valid_file
|
||||
config_regex =/^(BUNDLE_.+): (?:['"](.*)['"]|(.+(?:\n(?!BUNDLE).+))|(.+))$/
|
||||
config_pairs = config_file.read.scan(config_regex).map{|m| m.compact.map { |n| n.gsub(/\n\s?/, "") } }
|
||||
Hash[config_pairs]
|
||||
else
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: duplicates Rubygems#normalize_uri
|
||||
# TODO: is this the correct place to validate mirror URIs?
|
||||
def normalize_uri(uri)
|
||||
uri = uri.to_s
|
||||
uri = "#{uri}/" unless uri =~ %r[/\Z]
|
||||
uri = URI(uri)
|
||||
raise ArgumentError, "Gem mirror sources must be absolute URIs (configured: #{mirror_source})" unless uri.absolute?
|
||||
uri
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
require 'bundler/shared_helpers'
|
||||
|
||||
if Bundler::SharedHelpers.in_bundle?
|
||||
require 'bundler'
|
||||
if STDOUT.tty?
|
||||
begin
|
||||
Bundler.setup
|
||||
rescue Bundler::BundlerError => e
|
||||
puts "\e[31m#{e.message}\e[0m"
|
||||
puts e.backtrace.join("\n") if ENV["DEBUG"]
|
||||
if e.is_a?(Bundler::GemNotFound)
|
||||
puts "\e[33mRun `bundle install` to install missing gems.\e[0m"
|
||||
end
|
||||
exit e.status_code
|
||||
end
|
||||
else
|
||||
Bundler.setup
|
||||
end
|
||||
|
||||
# Add bundler to the load path after disabling system gems
|
||||
bundler_lib = File.expand_path("../..", __FILE__)
|
||||
$LOAD_PATH.unshift(bundler_lib) unless $LOAD_PATH.include?(bundler_lib)
|
||||
end
|
|
@ -0,0 +1,110 @@
|
|||
require 'pathname'
|
||||
require 'rubygems'
|
||||
|
||||
require 'bundler/constants'
|
||||
require 'bundler/rubygems_integration'
|
||||
require 'bundler/current_ruby'
|
||||
|
||||
module Gem
|
||||
class Dependency
|
||||
if !instance_methods.map { |m| m.to_s }.include?("requirement")
|
||||
def requirement
|
||||
version_requirements
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Bundler
|
||||
module SharedHelpers
|
||||
attr_accessor :gem_loaded
|
||||
|
||||
def default_gemfile
|
||||
gemfile = find_gemfile
|
||||
raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
|
||||
Pathname.new(gemfile)
|
||||
end
|
||||
|
||||
def default_lockfile
|
||||
Pathname.new("#{default_gemfile}.lock")
|
||||
end
|
||||
|
||||
def in_bundle?
|
||||
find_gemfile
|
||||
end
|
||||
|
||||
if Bundler.current_ruby.mswin? || Bundler.current_ruby.jruby?
|
||||
require 'monitor'
|
||||
@chdir_monitor = Monitor.new
|
||||
def chdir(dir, &blk)
|
||||
@chdir_monitor.synchronize do
|
||||
Dir.chdir dir, &blk
|
||||
end
|
||||
end
|
||||
|
||||
def pwd
|
||||
@chdir_monitor.synchronize do
|
||||
Dir.pwd
|
||||
end
|
||||
end
|
||||
else
|
||||
def chdir(dir, &blk)
|
||||
Dir.chdir dir, &blk
|
||||
end
|
||||
|
||||
def pwd
|
||||
Dir.pwd
|
||||
end
|
||||
end
|
||||
|
||||
def with_clean_git_env(&block)
|
||||
keys = %w[GIT_DIR GIT_WORK_TREE]
|
||||
old_env = keys.inject({}) do |h, k|
|
||||
h.update(k => ENV[k])
|
||||
end
|
||||
|
||||
keys.each {|key| ENV.delete(key) }
|
||||
|
||||
block.call
|
||||
ensure
|
||||
keys.each {|key| ENV[key] = old_env[key] }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_gemfile
|
||||
given = ENV['BUNDLE_GEMFILE']
|
||||
return given if given && !given.empty?
|
||||
|
||||
previous = nil
|
||||
current = File.expand_path(SharedHelpers.pwd)
|
||||
|
||||
until !File.directory?(current) || current == previous
|
||||
if ENV['BUNDLE_SPEC_RUN']
|
||||
# avoid stepping above the tmp directory when testing
|
||||
return nil if File.file?(File.join(current, 'bundler.gemspec'))
|
||||
end
|
||||
|
||||
# otherwise return the Gemfile if it's there
|
||||
filename = File.join(current, 'Gemfile')
|
||||
return filename if File.file?(filename)
|
||||
current, previous = File.expand_path("..", current), current
|
||||
end
|
||||
end
|
||||
|
||||
def clean_load_path
|
||||
# handle 1.9 where system gems are always on the load path
|
||||
if defined?(::Gem)
|
||||
me = File.expand_path("../../", __FILE__)
|
||||
$LOAD_PATH.reject! do |p|
|
||||
next if File.expand_path(p) =~ /^#{Regexp.escape(me)}/
|
||||
p != File.dirname(__FILE__) &&
|
||||
Bundler.rubygems.gem_path.any?{|gp| p =~ /^#{Regexp.escape(gp)}/ }
|
||||
end
|
||||
$LOAD_PATH.uniq!
|
||||
end
|
||||
end
|
||||
|
||||
extend self
|
||||
end
|
||||
end
|
|
@ -0,0 +1,63 @@
|
|||
module Bundler
|
||||
class SimilarityDetector
|
||||
SimilarityScore = Struct.new(:string, :distance)
|
||||
|
||||
# initialize with an array of words to be matched against
|
||||
def initialize(corpus)
|
||||
@corpus = corpus
|
||||
end
|
||||
|
||||
# return an array of words similar to 'word' from the corpus
|
||||
def similar_words(word, limit=3)
|
||||
words_by_similarity = @corpus.map{|w| SimilarityScore.new(w, levenshtein_distance(word, w))}
|
||||
words_by_similarity.select{|s| s.distance<=limit}.sort_by(&:distance).map(&:string)
|
||||
end
|
||||
|
||||
# return the result of 'similar_words', concatenated into a list
|
||||
# (eg "a, b, or c")
|
||||
def similar_word_list(word, limit=3)
|
||||
words = similar_words(word,limit)
|
||||
if words.length==1
|
||||
words[0]
|
||||
elsif words.length>1
|
||||
[words[0..-2].join(', '), words[-1]].join(' or ')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
# http://www.informit.com/articles/article.aspx?p=683059&seqNum=36
|
||||
def levenshtein_distance(this, that, ins=2, del=2, sub=1)
|
||||
# ins, del, sub are weighted costs
|
||||
return nil if this.nil?
|
||||
return nil if that.nil?
|
||||
dm = [] # distance matrix
|
||||
|
||||
# Initialize first row values
|
||||
dm[0] = (0..this.length).collect { |i| i * ins }
|
||||
fill = [0] * (this.length - 1)
|
||||
|
||||
# Initialize first column values
|
||||
for i in 1..that.length
|
||||
dm[i] = [i * del, fill.flatten]
|
||||
end
|
||||
|
||||
# populate matrix
|
||||
for i in 1..that.length
|
||||
for j in 1..this.length
|
||||
# critical comparison
|
||||
dm[i][j] = [
|
||||
dm[i-1][j-1] +
|
||||
(this[j-1] == that[i-1] ? 0 : sub),
|
||||
dm[i][j-1] + ins,
|
||||
dm[i-1][j] + del
|
||||
].min
|
||||
end
|
||||
end
|
||||
|
||||
# The last value in matrix is the Levenshtein distance between the strings
|
||||
dm[that.length][this.length]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
module Bundler
|
||||
class Source
|
||||
autoload :Rubygems, 'bundler/source/rubygems'
|
||||
autoload :Path, 'bundler/source/path'
|
||||
autoload :Git, 'bundler/source/git'
|
||||
|
||||
def self.mirror_for(uri)
|
||||
uri = URI(uri.to_s) unless uri.is_a?(URI)
|
||||
|
||||
# Settings keys are all downcased
|
||||
mirrors = Bundler.settings.gem_mirrors
|
||||
normalized_key = URI(uri.to_s.downcase)
|
||||
|
||||
mirrors[normalized_key] || uri
|
||||
end
|
||||
|
||||
attr_accessor :dependency_names
|
||||
|
||||
def unmet_deps
|
||||
specs.unmet_dependency_names
|
||||
end
|
||||
|
||||
def version_message(spec)
|
||||
locked_spec = Bundler.locked_gems.specs.find { |s| s.name == spec.name } if Bundler.locked_gems
|
||||
locked_spec_version = locked_spec.version if locked_spec
|
||||
message = "#{spec.name} #{spec.version}"
|
||||
if locked_spec_version && spec.version != locked_spec_version
|
||||
message << " (was #{locked_spec_version})"
|
||||
end
|
||||
message
|
||||
end
|
||||
|
||||
def can_lock?(spec)
|
||||
spec.source == self
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,290 @@
|
|||
require 'fileutils'
|
||||
require 'uri'
|
||||
require 'digest/sha1'
|
||||
|
||||
module Bundler
|
||||
class Source
|
||||
|
||||
class Git < Path
|
||||
autoload :GitProxy, 'bundler/source/git/git_proxy'
|
||||
|
||||
attr_reader :uri, :ref, :branch, :options, :submodules
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
@glob = options["glob"] || DEFAULT_GLOB
|
||||
|
||||
@allow_cached = false
|
||||
@allow_remote = false
|
||||
|
||||
# Stringify options that could be set as symbols
|
||||
%w(ref branch tag revision).each{|k| options[k] = options[k].to_s if options[k] }
|
||||
|
||||
@uri = options["uri"]
|
||||
@branch = options["branch"]
|
||||
@ref = options["ref"] || options["branch"] || options["tag"] || 'master'
|
||||
@submodules = options["submodules"]
|
||||
@name = options["name"]
|
||||
@version = options["version"]
|
||||
|
||||
@copied = false
|
||||
@local = false
|
||||
end
|
||||
|
||||
def self.from_lock(options)
|
||||
new(options.merge("uri" => options.delete("remote")))
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = "GIT\n"
|
||||
out << " remote: #{@uri}\n"
|
||||
out << " revision: #{revision}\n"
|
||||
%w(ref branch tag submodules).each do |opt|
|
||||
out << " #{opt}: #{options[opt]}\n" if options[opt]
|
||||
end
|
||||
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
||||
out << " specs:\n"
|
||||
end
|
||||
|
||||
def eql?(o)
|
||||
o.is_a?(Git) &&
|
||||
uri == o.uri &&
|
||||
ref == o.ref &&
|
||||
branch == o.branch &&
|
||||
name == o.name &&
|
||||
version == o.version &&
|
||||
submodules == o.submodules
|
||||
end
|
||||
|
||||
alias == eql?
|
||||
|
||||
def to_s
|
||||
at = if local?
|
||||
path
|
||||
elsif options["ref"]
|
||||
shortref_for_display(options["ref"])
|
||||
else
|
||||
ref
|
||||
end
|
||||
"#{uri} (at #{at})"
|
||||
end
|
||||
|
||||
def name
|
||||
File.basename(@uri, '.git')
|
||||
end
|
||||
|
||||
# This is the path which is going to contain a specific
|
||||
# checkout of the git repository. When using local git
|
||||
# repos, this is set to the local repo.
|
||||
def install_path
|
||||
@install_path ||= begin
|
||||
git_scope = "#{base_name}-#{shortref_for_path(revision)}"
|
||||
path = Bundler.install_path.join(git_scope)
|
||||
|
||||
if !path.exist? && Bundler.requires_sudo?
|
||||
Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope)
|
||||
else
|
||||
path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias :path :install_path
|
||||
|
||||
def extension_dir_name
|
||||
"#{base_name}-#{shortref_for_path(revision)}"
|
||||
end
|
||||
|
||||
def unlock!
|
||||
git_proxy.revision = nil
|
||||
@unlocked = true
|
||||
end
|
||||
|
||||
def local_override!(path)
|
||||
return false if local?
|
||||
|
||||
path = Pathname.new(path)
|
||||
path = path.expand_path(Bundler.root) unless path.relative?
|
||||
|
||||
unless options["branch"] || Bundler.settings[:disable_local_branch_check]
|
||||
raise GitError, "Cannot use local override for #{name} at #{path} because " \
|
||||
":branch is not specified in Gemfile. Specify a branch or use " \
|
||||
"`bundle config --delete` to remove the local override"
|
||||
end
|
||||
|
||||
unless path.exist?
|
||||
raise GitError, "Cannot use local override for #{name} because #{path} " \
|
||||
"does not exist. Check `bundle config --delete` to remove the local override"
|
||||
end
|
||||
|
||||
set_local!(path)
|
||||
|
||||
# Create a new git proxy without the cached revision
|
||||
# so the Gemfile.lock always picks up the new revision.
|
||||
@git_proxy = GitProxy.new(path, uri, ref)
|
||||
|
||||
if git_proxy.branch != options["branch"] && !Bundler.settings[:disable_local_branch_check]
|
||||
raise GitError, "Local override for #{name} at #{path} is using branch " \
|
||||
"#{git_proxy.branch} but Gemfile specifies #{options["branch"]}"
|
||||
end
|
||||
|
||||
changed = cached_revision && cached_revision != git_proxy.revision
|
||||
|
||||
if changed && !@unlocked && !git_proxy.contains?(cached_revision)
|
||||
raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \
|
||||
"but the current branch in your local override for #{name} does not contain such commit. " \
|
||||
"Please make sure your branch is up to date."
|
||||
end
|
||||
|
||||
changed
|
||||
end
|
||||
|
||||
# TODO: actually cache git specs
|
||||
def specs(*)
|
||||
if has_app_cache? && !local?
|
||||
set_local!(app_cache_path)
|
||||
end
|
||||
|
||||
if requires_checkout? && !@copied
|
||||
git_proxy.checkout
|
||||
git_proxy.copy_to(install_path, submodules)
|
||||
serialize_gemspecs_in(install_path)
|
||||
@copied = true
|
||||
end
|
||||
|
||||
local_specs
|
||||
end
|
||||
|
||||
def install(spec)
|
||||
debug = nil
|
||||
if requires_checkout? && !@copied
|
||||
debug = " * Checking out revision: #{ref}"
|
||||
git_proxy.copy_to(install_path, submodules)
|
||||
serialize_gemspecs_in(install_path)
|
||||
@copied = true
|
||||
end
|
||||
generate_bin(spec)
|
||||
if requires_checkout? && spec.post_install_message
|
||||
Installer.post_install_messages[spec.name] = spec.post_install_message
|
||||
end
|
||||
["Using #{version_message(spec)} from #{to_s}", nil, debug]
|
||||
end
|
||||
|
||||
def cache(spec, custom_path = nil)
|
||||
app_cache_path = app_cache_path(custom_path)
|
||||
return unless Bundler.settings[:cache_all]
|
||||
return if path == app_cache_path
|
||||
cached!
|
||||
FileUtils.rm_rf(app_cache_path)
|
||||
git_proxy.checkout if requires_checkout?
|
||||
git_proxy.copy_to(app_cache_path, @submodules)
|
||||
serialize_gemspecs_in(app_cache_path)
|
||||
end
|
||||
|
||||
def load_spec_files
|
||||
super
|
||||
rescue PathError => e
|
||||
Bundler.ui.trace e
|
||||
raise GitError, "#{to_s} is not yet checked out. Run `bundle install` first."
|
||||
end
|
||||
|
||||
# This is the path which is going to contain a cache
|
||||
# of the git repository. When using the same git repository
|
||||
# across different projects, this cache will be shared.
|
||||
# When using local git repos, this is set to the local repo.
|
||||
def cache_path
|
||||
@cache_path ||= begin
|
||||
git_scope = "#{base_name}-#{uri_hash}"
|
||||
|
||||
if Bundler.requires_sudo?
|
||||
Bundler.user_bundle_path.join("cache/git", git_scope)
|
||||
else
|
||||
Bundler.cache.join("git", git_scope)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def app_cache_dirname
|
||||
"#{base_name}-#{shortref_for_path(cached_revision || revision)}"
|
||||
end
|
||||
|
||||
def revision
|
||||
git_proxy.revision
|
||||
end
|
||||
|
||||
def allow_git_ops?
|
||||
@allow_remote || @allow_cached
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def serialize_gemspecs_in(destination)
|
||||
expanded_path = destination.expand_path(Bundler.root)
|
||||
Dir["#{expanded_path}/#{@glob}"].each do |spec_path|
|
||||
# Evaluate gemspecs and cache the result. Gemspecs
|
||||
# in git might require git or other dependencies.
|
||||
# The gemspecs we cache should already be evaluated.
|
||||
spec = Bundler.load_gemspec(spec_path)
|
||||
next unless spec
|
||||
File.open(spec_path, 'wb') {|file| file.write(spec.to_ruby) }
|
||||
end
|
||||
end
|
||||
|
||||
def set_local!(path)
|
||||
@local = true
|
||||
@local_specs = @git_proxy = nil
|
||||
@cache_path = @install_path = path
|
||||
end
|
||||
|
||||
def has_app_cache?
|
||||
cached_revision && super
|
||||
end
|
||||
|
||||
def local?
|
||||
@local
|
||||
end
|
||||
|
||||
def requires_checkout?
|
||||
allow_git_ops? && !local?
|
||||
end
|
||||
|
||||
def base_name
|
||||
File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*},''),".git")
|
||||
end
|
||||
|
||||
def shortref_for_display(ref)
|
||||
ref[0..6]
|
||||
end
|
||||
|
||||
def shortref_for_path(ref)
|
||||
ref[0..11]
|
||||
end
|
||||
|
||||
def uri_hash
|
||||
if uri =~ %r{^\w+://(\w+@)?}
|
||||
# Downcase the domain component of the URI
|
||||
# and strip off a trailing slash, if one is present
|
||||
input = URI.parse(uri).normalize.to_s.sub(%r{/$},'')
|
||||
else
|
||||
# If there is no URI scheme, assume it is an ssh/git URI
|
||||
input = uri
|
||||
end
|
||||
Digest::SHA1.hexdigest(input)
|
||||
end
|
||||
|
||||
def cached_revision
|
||||
options["revision"]
|
||||
end
|
||||
|
||||
def cached?
|
||||
cache_path.exist?
|
||||
end
|
||||
|
||||
def git_proxy
|
||||
@git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision, self)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,158 @@
|
|||
module Bundler
|
||||
class Source
|
||||
class Git < Path
|
||||
|
||||
class GitNotInstalledError < GitError
|
||||
def initialize
|
||||
msg = "You need to install git to be able to use gems from git repositories. "
|
||||
msg << "For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git"
|
||||
super msg
|
||||
end
|
||||
end
|
||||
|
||||
class GitNotAllowedError < GitError
|
||||
def initialize(command)
|
||||
msg = "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, "
|
||||
msg << "this error message could probably be more useful. Please submit a ticket at http://github.com/bundler/bundler/issues "
|
||||
msg << "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
|
||||
super msg
|
||||
end
|
||||
end
|
||||
|
||||
class GitCommandError < GitError
|
||||
def initialize(command, path = nil)
|
||||
msg = "Git error: command `git #{command}` in directory #{Dir.pwd} has failed."
|
||||
msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path && path.exist?
|
||||
super msg
|
||||
end
|
||||
end
|
||||
|
||||
# The GitProxy is responsible to interact with git repositories.
|
||||
# All actions required by the Git source is encapsulated in this
|
||||
# object.
|
||||
class GitProxy
|
||||
attr_accessor :path, :uri, :ref
|
||||
attr_writer :revision
|
||||
|
||||
def initialize(path, uri, ref, revision = nil, git = nil)
|
||||
@path = path
|
||||
@uri = uri
|
||||
@ref = ref
|
||||
@revision = revision
|
||||
@git = git
|
||||
raise GitNotInstalledError.new if allow? && !Bundler.git_present?
|
||||
end
|
||||
|
||||
def revision
|
||||
@revision ||= allowed_in_path { git("rev-parse #{ref}").strip }
|
||||
end
|
||||
|
||||
def branch
|
||||
@branch ||= allowed_in_path do
|
||||
git("branch") =~ /^\* (.*)$/ && $1.strip
|
||||
end
|
||||
end
|
||||
|
||||
def contains?(commit)
|
||||
allowed_in_path do
|
||||
result = git_null("branch --contains #{commit}")
|
||||
$? == 0 && result =~ /^\* (.*)$/
|
||||
end
|
||||
end
|
||||
|
||||
def checkout
|
||||
if path.exist?
|
||||
return if has_revision_cached?
|
||||
Bundler.ui.confirm "Updating #{uri}"
|
||||
in_path do
|
||||
git_retry %|fetch --force --quiet --tags #{uri_escaped} "refs/heads/*:refs/heads/*"|
|
||||
end
|
||||
else
|
||||
Bundler.ui.info "Fetching #{uri}"
|
||||
FileUtils.mkdir_p(path.dirname)
|
||||
git_retry %|clone #{uri_escaped} "#{path}" --bare --no-hardlinks --quiet|
|
||||
end
|
||||
end
|
||||
|
||||
def copy_to(destination, submodules=false)
|
||||
unless File.exist?(destination.join(".git"))
|
||||
FileUtils.mkdir_p(destination.dirname)
|
||||
FileUtils.rm_rf(destination)
|
||||
git_retry %|clone --no-checkout --quiet "#{path}" "#{destination}"|
|
||||
File.chmod((0777 & ~File.umask), destination)
|
||||
end
|
||||
|
||||
SharedHelpers.chdir(destination) do
|
||||
git_retry %|fetch --force --quiet --tags "#{path}"|
|
||||
git "reset --hard #{@revision}"
|
||||
|
||||
if submodules
|
||||
git_retry "submodule update --init --recursive"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# TODO: Do not rely on /dev/null.
|
||||
# Given that open3 is not cross platform until Ruby 1.9.3,
|
||||
# the best solution is to pipe to /dev/null if it exists.
|
||||
# If it doesn't, everything will work fine, but the user
|
||||
# will get the $stderr messages as well.
|
||||
def git_null(command)
|
||||
git("#{command} 2>#{Bundler::NULL}", false)
|
||||
end
|
||||
|
||||
def git_retry(command)
|
||||
Bundler::Retry.new("git #{command}", GitNotAllowedError).attempts do
|
||||
git(command)
|
||||
end
|
||||
end
|
||||
|
||||
def git(command, check_errors=true)
|
||||
raise GitNotAllowedError.new(command) unless allow?
|
||||
out = SharedHelpers.with_clean_git_env { %x{git #{command}} }
|
||||
raise GitCommandError.new(command, path) if check_errors && !$?.success?
|
||||
out
|
||||
end
|
||||
|
||||
def has_revision_cached?
|
||||
return unless @revision
|
||||
in_path { git("cat-file -e #{@revision}") }
|
||||
true
|
||||
rescue GitError
|
||||
false
|
||||
end
|
||||
|
||||
# Escape the URI for git commands
|
||||
def uri_escaped
|
||||
if Bundler::WINDOWS
|
||||
# Windows quoting requires double quotes only, with double quotes
|
||||
# inside the string escaped by being doubled.
|
||||
'"' + uri.gsub('"') {|s| '""'} + '"'
|
||||
else
|
||||
# Bash requires single quoted strings, with the single quotes escaped
|
||||
# by ending the string, escaping the quote, and restarting the string.
|
||||
"'" + uri.gsub("'") {|s| "'\\''"} + "'"
|
||||
end
|
||||
end
|
||||
|
||||
def allow?
|
||||
@git ? @git.allow_git_ops? : true
|
||||
end
|
||||
|
||||
def in_path(&blk)
|
||||
checkout unless path.exist?
|
||||
SharedHelpers.chdir(path, &blk)
|
||||
end
|
||||
|
||||
def allowed_in_path
|
||||
return in_path { yield } if allow?
|
||||
raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,225 @@
|
|||
module Bundler
|
||||
class Source
|
||||
|
||||
class Path < Source
|
||||
autoload :Installer, 'bundler/source/path/installer'
|
||||
|
||||
attr_reader :path, :options
|
||||
attr_writer :name
|
||||
attr_accessor :version
|
||||
|
||||
DEFAULT_GLOB = "{,*,*/*}.gemspec"
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
@glob = options["glob"] || DEFAULT_GLOB
|
||||
|
||||
@allow_cached = false
|
||||
@allow_remote = false
|
||||
|
||||
if options["path"]
|
||||
@path = Pathname.new(options["path"])
|
||||
@path = expand(@path) unless @path.relative?
|
||||
end
|
||||
|
||||
@name = options["name"]
|
||||
@version = options["version"]
|
||||
|
||||
# Stores the original path. If at any point we move to the
|
||||
# cached directory, we still have the original path to copy from.
|
||||
@original_path = @path
|
||||
end
|
||||
|
||||
def remote!
|
||||
@allow_remote = true
|
||||
end
|
||||
|
||||
def cached!
|
||||
@allow_cached = true
|
||||
end
|
||||
|
||||
def self.from_lock(options)
|
||||
new(options.merge("path" => options.delete("remote")))
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = "PATH\n"
|
||||
out << " remote: #{relative_path}\n"
|
||||
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
||||
out << " specs:\n"
|
||||
end
|
||||
|
||||
def to_s
|
||||
"source at #{@path}"
|
||||
end
|
||||
|
||||
def hash
|
||||
self.class.hash
|
||||
end
|
||||
|
||||
def eql?(o)
|
||||
o.instance_of?(Path) &&
|
||||
expand(path) == expand(o.path) &&
|
||||
version == o.version
|
||||
end
|
||||
|
||||
alias == eql?
|
||||
|
||||
def name
|
||||
File.basename(expand(path).to_s)
|
||||
end
|
||||
|
||||
def install(spec)
|
||||
generate_bin(spec, :disable_extensions)
|
||||
["Using #{version_message(spec)} from #{to_s}", nil]
|
||||
end
|
||||
|
||||
def cache(spec, custom_path = nil)
|
||||
app_cache_path = app_cache_path(custom_path)
|
||||
return unless Bundler.settings[:cache_all]
|
||||
return if expand(@original_path).to_s.index(Bundler.root.to_s) == 0
|
||||
|
||||
unless @original_path.exist?
|
||||
raise GemNotFound, "Can't cache gem #{version_message(spec)} because #{to_s} is missing!"
|
||||
end
|
||||
|
||||
FileUtils.rm_rf(app_cache_path)
|
||||
FileUtils.cp_r("#{@original_path}/.", app_cache_path)
|
||||
FileUtils.touch(app_cache_path.join(".bundlecache"))
|
||||
end
|
||||
|
||||
def local_specs(*)
|
||||
@local_specs ||= load_spec_files
|
||||
end
|
||||
|
||||
def specs
|
||||
if has_app_cache?
|
||||
@path = app_cache_path
|
||||
end
|
||||
local_specs
|
||||
end
|
||||
|
||||
def app_cache_dirname
|
||||
name
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expand(somepath)
|
||||
somepath.expand_path(Bundler.root)
|
||||
rescue ArgumentError => e
|
||||
Bundler.ui.debug(e)
|
||||
raise PathError, "There was an error while trying to use the path " \
|
||||
"`#{somepath}`.\nThe error message was: #{e.message}."
|
||||
end
|
||||
|
||||
def app_cache_path(custom_path = nil)
|
||||
@app_cache_path ||= Bundler.app_cache(custom_path).join(app_cache_dirname)
|
||||
end
|
||||
|
||||
def has_app_cache?
|
||||
SharedHelpers.in_bundle? && app_cache_path.exist?
|
||||
end
|
||||
|
||||
def load_spec_files
|
||||
index = Index.new
|
||||
expanded_path = expand(path)
|
||||
|
||||
if File.directory?(expanded_path)
|
||||
Dir["#{expanded_path}/#{@glob}"].each do |file|
|
||||
spec = Bundler.load_gemspec(file)
|
||||
if spec
|
||||
spec.loaded_from = file.to_s
|
||||
spec.source = self
|
||||
index << spec
|
||||
end
|
||||
end
|
||||
|
||||
if index.empty? && @name && @version
|
||||
index << Gem::Specification.new do |s|
|
||||
s.name = @name
|
||||
s.source = self
|
||||
s.version = Gem::Version.new(@version)
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.summary = "Fake gemspec for #{@name}"
|
||||
s.relative_loaded_from = "#{@name}.gemspec"
|
||||
s.authors = ["no one"]
|
||||
if expanded_path.join("bin").exist?
|
||||
executables = expanded_path.join("bin").children
|
||||
executables.reject!{|p| File.directory?(p) }
|
||||
s.executables = executables.map{|c| c.basename.to_s }
|
||||
end
|
||||
end
|
||||
end
|
||||
elsif File.exist?(expanded_path)
|
||||
raise PathError, "The path `#{expanded_path}` is not a directory."
|
||||
else
|
||||
raise PathError, "The path `#{expanded_path}` does not exist."
|
||||
end
|
||||
|
||||
index
|
||||
end
|
||||
|
||||
def relative_path
|
||||
if path.to_s.match(%r{^#{Regexp.escape Bundler.root.to_s}})
|
||||
return path.relative_path_from(Bundler.root)
|
||||
end
|
||||
path
|
||||
end
|
||||
|
||||
def generate_bin(spec, disable_extensions = false)
|
||||
gem_dir = Pathname.new(spec.full_gem_path)
|
||||
|
||||
# Some gem authors put absolute paths in their gemspec
|
||||
# and we have to save them from themselves
|
||||
spec.files = spec.files.map do |p|
|
||||
next if File.directory?(p)
|
||||
begin
|
||||
Pathname.new(p).relative_path_from(gem_dir).to_s
|
||||
rescue ArgumentError
|
||||
p
|
||||
end
|
||||
end.compact
|
||||
|
||||
gem_file = Bundler.rubygems.build_gem gem_dir, spec
|
||||
|
||||
installer = Path::Installer.new(spec, :env_shebang => false)
|
||||
run_hooks(:pre_install, installer)
|
||||
installer.build_extensions unless disable_extensions
|
||||
run_hooks(:post_build, installer)
|
||||
installer.generate_bin
|
||||
run_hooks(:post_install, installer)
|
||||
rescue Gem::InvalidSpecificationException => e
|
||||
Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
|
||||
"This prevents bundler from installing bins or native extensions, but " \
|
||||
"that may not affect its functionality."
|
||||
|
||||
if !spec.extensions.empty? && !spec.email.empty?
|
||||
Bundler.ui.warn "If you need to use this package without installing it from a gem " \
|
||||
"repository, please contact #{spec.email} and ask them " \
|
||||
"to modify their .gemspec so it can work with `gem build`."
|
||||
end
|
||||
|
||||
Bundler.ui.warn "The validation message from Rubygems was:\n #{e.message}"
|
||||
ensure
|
||||
if gem_dir && gem_file
|
||||
FileUtils.rm_rf(gem_dir.join gem_file)
|
||||
end
|
||||
end
|
||||
|
||||
def run_hooks(type, installer)
|
||||
hooks_meth = "#{type}_hooks"
|
||||
return unless Gem.respond_to?(hooks_meth)
|
||||
Gem.send(hooks_meth).each do |hook|
|
||||
result = hook.call(installer)
|
||||
if result == false
|
||||
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
|
||||
message = "#{type} hook#{location} failed for #{installer.spec.full_name}"
|
||||
raise InstallHookError, message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,38 @@
|
|||
module Bundler
|
||||
class Source
|
||||
class Path
|
||||
|
||||
class Installer < Bundler::GemInstaller
|
||||
def initialize(spec, options = {})
|
||||
@spec = spec
|
||||
@tmp_bin_dir = "#{Bundler.tmp(spec.full_name)}/bin"
|
||||
@gem_bin_dir = "#{Bundler.rubygems.gem_dir}/bin"
|
||||
@bin_dir = Bundler.requires_sudo? ? @tmp_bin_dir : @gem_bin_dir
|
||||
@gem_dir = Bundler.rubygems.path(spec.full_gem_path)
|
||||
@wrappers = options[:wrappers] || true
|
||||
@env_shebang = options[:env_shebang] || true
|
||||
@format_executable = options[:format_executable] || false
|
||||
@build_args = options[:build_args] || Bundler.rubygems.build_args
|
||||
end
|
||||
|
||||
def generate_bin
|
||||
return if spec.executables.nil? || spec.executables.empty?
|
||||
|
||||
if Bundler.requires_sudo?
|
||||
FileUtils.mkdir_p(@tmp_bin_dir) unless File.exist?(@tmp_bin_dir)
|
||||
end
|
||||
|
||||
super
|
||||
|
||||
if Bundler.requires_sudo?
|
||||
Bundler.mkdir_p @gem_bin_dir
|
||||
spec.executables.each do |exe|
|
||||
Bundler.sudo "cp -R #{@tmp_bin_dir}/#{exe} #{@gem_bin_dir}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,350 @@
|
|||
require 'uri'
|
||||
require 'rubygems/user_interaction'
|
||||
require 'rubygems/spec_fetcher'
|
||||
|
||||
module Bundler
|
||||
class Source
|
||||
class Rubygems < Source
|
||||
API_REQUEST_LIMIT = 100 # threshold for switching back to the modern index instead of fetching every spec
|
||||
|
||||
attr_reader :remotes, :caches
|
||||
|
||||
def initialize(options = {})
|
||||
@options = options
|
||||
@remotes = []
|
||||
@dependency_names = []
|
||||
@allow_remote = false
|
||||
@allow_cached = false
|
||||
@caches = [Bundler.app_cache, *Bundler.rubygems.gem_cache]
|
||||
|
||||
Array(options["remotes"] || []).reverse_each{|r| add_remote(r) }
|
||||
end
|
||||
|
||||
def remote!
|
||||
@allow_remote = true
|
||||
end
|
||||
|
||||
def cached!
|
||||
@allow_cached = true
|
||||
end
|
||||
|
||||
def hash
|
||||
@remotes.hash
|
||||
end
|
||||
|
||||
def eql?(o)
|
||||
o.is_a?(Rubygems) && remotes_equal?(o.remotes)
|
||||
end
|
||||
|
||||
alias == eql?
|
||||
|
||||
def can_lock?(spec)
|
||||
spec.source.is_a?(Rubygems)
|
||||
end
|
||||
|
||||
def options
|
||||
{ "remotes" => @remotes.map { |r| r.to_s } }
|
||||
end
|
||||
|
||||
def self.from_lock(options)
|
||||
new(options)
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = "GEM\n"
|
||||
out << remotes.map { |remote|
|
||||
" remote: #{suppress_configured_credentials remote}\n"
|
||||
}.join
|
||||
out << " specs:\n"
|
||||
end
|
||||
|
||||
def to_s
|
||||
remote_names = self.remotes.map { |r| r.to_s }.join(', ')
|
||||
"rubygems repository #{remote_names}"
|
||||
end
|
||||
alias_method :name, :to_s
|
||||
|
||||
def specs
|
||||
@specs ||= begin
|
||||
# remote_specs usually generates a way larger Index than the other
|
||||
# sources, and large_idx.use small_idx is way faster than
|
||||
# small_idx.use large_idx.
|
||||
idx = @allow_remote ? remote_specs.dup : Index.new
|
||||
idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
|
||||
idx.use(installed_specs, :override_dupes)
|
||||
idx
|
||||
end
|
||||
end
|
||||
|
||||
def install(spec)
|
||||
return ["Using #{version_message(spec)}", nil] if installed_specs[spec].any?
|
||||
|
||||
# Download the gem to get the spec, because some specs that are returned
|
||||
# by rubygems.org are broken and wrong.
|
||||
if spec.source_uri
|
||||
# Check for this spec from other sources
|
||||
uris = [spec.source_uri]
|
||||
uris += source_uris_for_spec(spec)
|
||||
uris.compact!
|
||||
uris.uniq!
|
||||
Installer.ambiguous_gems << [spec.name, *uris] if uris.length > 1
|
||||
|
||||
s = Bundler.rubygems.spec_from_gem(fetch_gem(spec), Bundler.settings["trust-policy"])
|
||||
spec.__swap__(s)
|
||||
end
|
||||
|
||||
path = cached_gem(spec)
|
||||
if Bundler.requires_sudo?
|
||||
install_path = Bundler.tmp(spec.full_name)
|
||||
bin_path = install_path.join("bin")
|
||||
else
|
||||
install_path = Bundler.rubygems.gem_dir
|
||||
bin_path = Bundler.system_bindir
|
||||
end
|
||||
|
||||
installed_spec = nil
|
||||
Bundler.rubygems.preserve_paths do
|
||||
installed_spec = Bundler::GemInstaller.new(path,
|
||||
:install_dir => install_path.to_s,
|
||||
:bin_dir => bin_path.to_s,
|
||||
:ignore_dependencies => true,
|
||||
:wrappers => true,
|
||||
:env_shebang => true
|
||||
).install
|
||||
end
|
||||
|
||||
# SUDO HAX
|
||||
if Bundler.requires_sudo?
|
||||
Bundler.rubygems.repository_subdirectories.each do |name|
|
||||
src = File.join(install_path, name, "*")
|
||||
dst = File.join(Bundler.rubygems.gem_dir, name)
|
||||
if name == "extensions" && Dir.glob(src).any?
|
||||
src = File.join(src, "*/*")
|
||||
ext_src = Dir.glob(src).first
|
||||
ext_src.gsub!(src[0..-6], '')
|
||||
dst = File.dirname(File.join(dst, ext_src))
|
||||
end
|
||||
Bundler.mkdir_p dst
|
||||
Bundler.sudo "cp -R #{src} #{dst}" if Dir[src].any?
|
||||
end
|
||||
|
||||
spec.executables.each do |exe|
|
||||
Bundler.mkdir_p Bundler.system_bindir
|
||||
Bundler.sudo "cp -R #{install_path}/bin/#{exe} #{Bundler.system_bindir}/"
|
||||
end
|
||||
end
|
||||
|
||||
spec.loaded_from = "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
|
||||
installed_spec.loaded_from = spec.loaded_from
|
||||
["Installing #{version_message(spec)}", spec.post_install_message]
|
||||
ensure
|
||||
if install_path && Bundler.requires_sudo?
|
||||
FileUtils.remove_entry_secure(install_path)
|
||||
end
|
||||
end
|
||||
|
||||
def cache(spec, custom_path = nil)
|
||||
if builtin_gem?(spec)
|
||||
cached_path = cached_built_in_gem(spec)
|
||||
else
|
||||
cached_path = cached_gem(spec)
|
||||
end
|
||||
raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
|
||||
return if File.dirname(cached_path) == Bundler.app_cache.to_s
|
||||
Bundler.ui.info " * #{File.basename(cached_path)}"
|
||||
FileUtils.cp(cached_path, Bundler.app_cache(custom_path))
|
||||
end
|
||||
|
||||
def cached_built_in_gem(spec)
|
||||
cached_path = cached_path(spec)
|
||||
if cached_path.nil?
|
||||
remote_spec = remote_specs.search(spec).first
|
||||
cached_path = fetch_gem(remote_spec)
|
||||
end
|
||||
cached_path
|
||||
end
|
||||
|
||||
def add_remote(source)
|
||||
uri = normalize_uri(source)
|
||||
@remotes.unshift(uri) unless @remotes.include?(uri)
|
||||
end
|
||||
|
||||
def replace_remotes(other_remotes)
|
||||
return false if other_remotes == @remotes
|
||||
|
||||
@remotes = []
|
||||
other_remotes.reverse_each do |r|
|
||||
add_remote r.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def unmet_deps
|
||||
if @allow_remote && api_fetchers.any?
|
||||
remote_specs.unmet_dependency_names
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def source_uris_for_spec(spec)
|
||||
specs.search_all(spec.name).map{|s| s.source_uri }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cached_gem(spec)
|
||||
cached_gem = cached_path(spec)
|
||||
unless cached_gem
|
||||
raise Bundler::GemNotFound, "Could not find #{spec.file_name} for installation"
|
||||
end
|
||||
cached_gem
|
||||
end
|
||||
|
||||
def cached_path(spec)
|
||||
possibilities = @caches.map { |p| "#{p}/#{spec.file_name}" }
|
||||
possibilities.find { |p| File.exist?(p) }
|
||||
end
|
||||
|
||||
def normalize_uri(uri)
|
||||
uri = uri.to_s
|
||||
uri = "#{uri}/" unless uri =~ %r'/$'
|
||||
uri = URI(uri)
|
||||
raise ArgumentError, "The source must be an absolute URI" unless uri.absolute?
|
||||
uri
|
||||
end
|
||||
|
||||
def suppress_configured_credentials(remote)
|
||||
remote_nouser = remote.dup.tap { |uri| uri.user = uri.password = nil }.to_s
|
||||
if remote.userinfo && remote.userinfo == Bundler.settings[remote_nouser]
|
||||
remote_nouser
|
||||
else
|
||||
remote
|
||||
end
|
||||
end
|
||||
|
||||
def installed_specs
|
||||
@installed_specs ||= begin
|
||||
idx = Index.new
|
||||
have_bundler = false
|
||||
Bundler.rubygems.all_specs.reverse.each do |spec|
|
||||
next if spec.name == 'bundler' && spec.version.to_s != VERSION
|
||||
have_bundler = true if spec.name == 'bundler'
|
||||
spec.source = self
|
||||
idx << spec
|
||||
end
|
||||
|
||||
# Always have bundler locally
|
||||
unless have_bundler
|
||||
# We're running bundler directly from the source
|
||||
# so, let's create a fake gemspec for it (it's a path)
|
||||
# gemspec
|
||||
bundler = Gem::Specification.new do |s|
|
||||
s.name = 'bundler'
|
||||
s.version = VERSION
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.source = self
|
||||
s.authors = ["bundler team"]
|
||||
s.loaded_from = File.expand_path("..", __FILE__)
|
||||
end
|
||||
idx << bundler
|
||||
end
|
||||
idx
|
||||
end
|
||||
end
|
||||
|
||||
def cached_specs
|
||||
@cached_specs ||= begin
|
||||
idx = installed_specs.dup
|
||||
|
||||
path = Bundler.app_cache
|
||||
Dir["#{path}/*.gem"].each do |gemfile|
|
||||
next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
|
||||
s ||= Bundler.rubygems.spec_from_gem(gemfile)
|
||||
s.source = self
|
||||
idx << s
|
||||
end
|
||||
end
|
||||
|
||||
idx
|
||||
end
|
||||
|
||||
def fetchers
|
||||
@fetchers ||= remotes.map do |url|
|
||||
Bundler::Fetcher.new(url)
|
||||
end
|
||||
end
|
||||
|
||||
def api_fetchers
|
||||
fetchers.select{|f| f.use_api }
|
||||
end
|
||||
|
||||
def remote_specs
|
||||
@remote_specs ||= Index.build do |idx|
|
||||
index_fetchers = fetchers - api_fetchers
|
||||
|
||||
# gather lists from non-api sites
|
||||
index_fetchers.each do |f|
|
||||
Bundler.ui.info "Fetching source index from #{f.uri}"
|
||||
idx.use f.specs(nil, self)
|
||||
end
|
||||
|
||||
# because ensuring we have all the gems we need involves downloading
|
||||
# the gemspecs of those gems, if the non-api sites contain more than
|
||||
# about 100 gems, we just treat all sites as non-api for speed.
|
||||
allow_api = idx.size < API_REQUEST_LIMIT && dependency_names.size < API_REQUEST_LIMIT
|
||||
|
||||
if allow_api
|
||||
api_fetchers.each do |f|
|
||||
Bundler.ui.info "Fetching gem metadata from #{f.uri}", Bundler.ui.debug?
|
||||
idx.use f.specs(dependency_names, self)
|
||||
Bundler.ui.info "" if !Bundler.ui.debug? # new line now that the dots are over
|
||||
end
|
||||
|
||||
if api_fetchers.any? && api_fetchers.all?{|f| f.use_api }
|
||||
# it's possible that gems from one source depend on gems from some
|
||||
# other source, so now we download gemspecs and iterate over those
|
||||
# dependencies, looking for gems we don't have info on yet.
|
||||
unmet = idx.unmet_dependency_names
|
||||
|
||||
# if there are any cross-site gems we missed, get them now
|
||||
api_fetchers.each do |f|
|
||||
Bundler.ui.info "Fetching additional metadata from #{f.uri}", Bundler.ui.debug?
|
||||
idx.use f.specs(unmet, self)
|
||||
Bundler.ui.info "" if !Bundler.ui.debug? # new line now that the dots are over
|
||||
end if unmet.any?
|
||||
else
|
||||
allow_api = false
|
||||
end
|
||||
end
|
||||
|
||||
if !allow_api
|
||||
api_fetchers.each do |f|
|
||||
Bundler.ui.info "Fetching source index from #{f.uri}"
|
||||
idx.use f.specs(nil, self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_gem(spec)
|
||||
return false unless spec.source_uri
|
||||
Fetcher.download_gem_from_uri(spec, spec.source_uri)
|
||||
end
|
||||
|
||||
def builtin_gem?(spec)
|
||||
# Ruby 2.1, where all included gems have this summary
|
||||
return true if spec.summary =~ /is bundled with Ruby/
|
||||
|
||||
# Ruby 2.0, where gemspecs are stored in specifications/default/
|
||||
spec.loaded_from && spec.loaded_from.include?("specifications/default/")
|
||||
end
|
||||
|
||||
def remotes_equal?(other_remotes)
|
||||
remotes.map(&method(:suppress_configured_credentials)) == other_remotes.map(&method(:suppress_configured_credentials))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,80 @@
|
|||
module Bundler
|
||||
class SourceList
|
||||
attr_reader :path_sources,
|
||||
:git_sources,
|
||||
:rubygems_sources
|
||||
|
||||
def initialize
|
||||
@path_sources = []
|
||||
@git_sources = []
|
||||
@rubygems_aggregate = Source::Rubygems.new
|
||||
@rubygems_sources = [@rubygems_aggregate]
|
||||
end
|
||||
|
||||
def add_path_source(options = {})
|
||||
add_source_to_list Source::Path.new(options), path_sources
|
||||
end
|
||||
|
||||
def add_git_source(options = {})
|
||||
add_source_to_list Source::Git.new(options), git_sources
|
||||
end
|
||||
|
||||
def add_rubygems_source(options = {})
|
||||
add_source_to_list Source::Rubygems.new(options), @rubygems_sources
|
||||
end
|
||||
|
||||
def add_rubygems_remote(uri)
|
||||
@rubygems_aggregate.add_remote(uri)
|
||||
@rubygems_aggregate
|
||||
end
|
||||
|
||||
def all_sources
|
||||
path_sources + git_sources + rubygems_sources
|
||||
end
|
||||
|
||||
def get(source)
|
||||
source_list_for(source).find { |s| source == s }
|
||||
end
|
||||
|
||||
def lock_sources
|
||||
lock_sources = (path_sources + git_sources).sort_by(&:to_s)
|
||||
lock_sources << combine_rubygems_sources
|
||||
end
|
||||
|
||||
def replace_sources!(replacement_sources)
|
||||
[path_sources, git_sources, rubygems_sources].each do |source_list|
|
||||
source_list.map! do |source|
|
||||
replacement_sources.find { |s| s == source } || source
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cached!
|
||||
all_sources.each(&:cached!)
|
||||
end
|
||||
|
||||
def remote!
|
||||
all_sources.each(&:remote!)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_source_to_list(source, list)
|
||||
list.unshift(source).uniq!
|
||||
source
|
||||
end
|
||||
|
||||
def source_list_for(source)
|
||||
case source
|
||||
when Source::Git then git_sources
|
||||
when Source::Path then path_sources
|
||||
when Source::Rubygems then rubygems_sources
|
||||
else raise ArgumentError, "Invalid source: #{source.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
def combine_rubygems_sources
|
||||
Source::Rubygems.new("remotes" => rubygems_sources.map(&:remotes).flatten.uniq.reverse)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,154 @@
|
|||
require 'tsort'
|
||||
require 'forwardable'
|
||||
|
||||
module Bundler
|
||||
class SpecSet
|
||||
extend Forwardable
|
||||
include TSort, Enumerable
|
||||
|
||||
def_delegators :@specs, :<<, :length, :add, :remove
|
||||
def_delegators :sorted, :each
|
||||
|
||||
def initialize(specs)
|
||||
@specs = specs.sort_by { |s| s.name }
|
||||
end
|
||||
|
||||
def for(dependencies, skip = [], check = false, match_current_platform = false)
|
||||
handled, deps, specs = {}, dependencies.dup, []
|
||||
skip << 'bundler'
|
||||
|
||||
until deps.empty?
|
||||
dep = deps.shift
|
||||
next if handled[dep] || skip.include?(dep.name)
|
||||
|
||||
spec = lookup[dep.name].find do |s|
|
||||
if match_current_platform
|
||||
Gem::Platform.match(s.platform)
|
||||
else
|
||||
s.match_platform(dep.__platform)
|
||||
end
|
||||
end
|
||||
|
||||
handled[dep] = true
|
||||
|
||||
if spec
|
||||
specs << spec
|
||||
|
||||
spec.dependencies.each do |d|
|
||||
next if d.type == :development
|
||||
d = DepProxy.new(d, dep.__platform) unless match_current_platform
|
||||
deps << d
|
||||
end
|
||||
elsif check
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if spec = lookup['bundler'].first
|
||||
specs << spec
|
||||
end
|
||||
|
||||
check ? true : SpecSet.new(specs)
|
||||
end
|
||||
|
||||
def valid_for?(deps)
|
||||
self.for(deps, [], true)
|
||||
end
|
||||
|
||||
def [](key)
|
||||
key = key.name if key.respond_to?(:name)
|
||||
lookup[key].reverse
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
@specs << value
|
||||
@lookup = nil
|
||||
@sorted = nil
|
||||
value
|
||||
end
|
||||
|
||||
def sort!
|
||||
self
|
||||
end
|
||||
|
||||
def to_a
|
||||
sorted.dup
|
||||
end
|
||||
|
||||
def to_hash
|
||||
lookup.dup
|
||||
end
|
||||
|
||||
def materialize(deps, missing_specs = nil)
|
||||
materialized = self.for(deps, [], false, true).to_a
|
||||
deps = materialized.map {|s| s.name }.uniq
|
||||
materialized.map! do |s|
|
||||
next s unless s.is_a?(LazySpecification)
|
||||
s.source.dependency_names = deps if s.source.respond_to?(:dependency_names=)
|
||||
spec = s.__materialize__
|
||||
if missing_specs
|
||||
missing_specs << s unless spec
|
||||
else
|
||||
raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec
|
||||
end
|
||||
spec if spec
|
||||
end
|
||||
SpecSet.new(materialized.compact)
|
||||
end
|
||||
|
||||
def merge(set)
|
||||
arr = sorted.dup
|
||||
set.each do |s|
|
||||
next if arr.any? { |s2| s2.name == s.name && s2.version == s.version && s2.platform == s.platform }
|
||||
arr << s
|
||||
end
|
||||
SpecSet.new(arr)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sorted
|
||||
rake = @specs.find { |s| s.name == 'rake' }
|
||||
begin
|
||||
@sorted ||= ([rake] + tsort).compact.uniq
|
||||
rescue TSort::Cyclic => error
|
||||
cgems = extract_circular_gems(error)
|
||||
raise CyclicDependencyError, "Your Gemfile requires gems that depend" \
|
||||
" depend on each other, creating an infinite loop. Please remove" \
|
||||
" either gem '#{cgems[1]}' or gem '#{cgems[0]}' and try again."
|
||||
end
|
||||
end
|
||||
|
||||
def extract_circular_gems(error)
|
||||
if Bundler.current_ruby.mri? && Bundler.current_ruby.on_19?
|
||||
error.message.scan(/(\w+) \([^)]/).flatten
|
||||
else
|
||||
error.message.scan(/@name="(.*?)"/).flatten
|
||||
end
|
||||
end
|
||||
|
||||
def lookup
|
||||
@lookup ||= begin
|
||||
lookup = Hash.new { |h,k| h[k] = [] }
|
||||
specs = @specs.sort_by do |s|
|
||||
s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s
|
||||
end
|
||||
specs.reverse_each do |s|
|
||||
lookup[s.name] << s
|
||||
end
|
||||
lookup
|
||||
end
|
||||
end
|
||||
|
||||
def tsort_each_node
|
||||
@specs.each { |s| yield s }
|
||||
end
|
||||
|
||||
def tsort_each_child(s)
|
||||
s.dependencies.sort_by { |d| d.name }.each do |d|
|
||||
next if d.type == :development
|
||||
lookup[d.name].each { |s2| yield s2 }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
# Ignore all files in this directory
|
|
@ -0,0 +1,14 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
|
||||
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
|
||||
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
|
||||
MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
|
||||
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
|
||||
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
|
||||
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
|
||||
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
|
||||
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
|
||||
CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
|
||||
lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
|
||||
AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
|
||||
VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
|
||||
ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
|
||||
KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
|
||||
ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
|
||||
MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
|
||||
ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
|
||||
b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
|
||||
bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
|
||||
U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
|
||||
A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
|
||||
I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
|
||||
wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
|
||||
AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
|
||||
oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
|
||||
BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
|
||||
dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
|
||||
MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
|
||||
b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
|
||||
dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
|
||||
MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
|
||||
E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
|
||||
MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
|
||||
hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
|
||||
95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
|
||||
2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
|
||||
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
|
||||
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
|
||||
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
|
||||
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
|
||||
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
|
||||
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
|
||||
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
|
||||
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
|
||||
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
|
||||
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
|
||||
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
|
||||
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
|
||||
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
|
||||
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
|
||||
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
|
||||
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
|
||||
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,41 @@
|
|||
require 'fileutils'
|
||||
|
||||
module Bundler
|
||||
module SSLCerts
|
||||
class CertificateManager
|
||||
attr_reader :bundler_cert_path, :bundler_certs, :rubygems_certs
|
||||
|
||||
def self.update_from!(rubygems_path)
|
||||
new(rubygems_path).update!
|
||||
end
|
||||
|
||||
def initialize(rubygems_path)
|
||||
rubygems_certs = File.join(rubygems_path, 'lib/rubygems/ssl_certs')
|
||||
@rubygems_certs = certificates_in(rubygems_certs)
|
||||
|
||||
@bundler_cert_path = File.expand_path("..", __FILE__)
|
||||
@bundler_certs = certificates_in(bundler_cert_path)
|
||||
end
|
||||
|
||||
def up_to_date?
|
||||
bundler_certs.zip(rubygems_certs).all? do |bc, rc|
|
||||
File.basename(bc) == File.basename(rc) && FileUtils.compare_file(bc, rc)
|
||||
end
|
||||
end
|
||||
|
||||
def update!
|
||||
return if up_to_date?
|
||||
|
||||
FileUtils.rm bundler_certs
|
||||
FileUtils.cp rubygems_certs, bundler_cert_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def certificates_in(path)
|
||||
Dir[File.join(path, "*.pem")].sort
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG['ruby_install_name'] %>
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application '<%= executable %>' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require 'pathname'
|
||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../<%= relative_gemfile_path %>",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
|
||||
require 'rubygems'
|
||||
require 'bundler/setup'
|
||||
|
||||
load Gem.bin_path('<%= spec.name %>', '<%= executable %>')
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG['ruby_install_name'] %>
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application '<%= executable %>' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
$:.unshift File.expand_path '../<%= standalone_path %>', __FILE__
|
||||
|
||||
require 'bundler/setup'
|
||||
load File.expand_path '../<%= executable_path %>', __FILE__
|
|
@ -0,0 +1,4 @@
|
|||
# A sample Gemfile
|
||||
source "https://rubygems.org"
|
||||
|
||||
# gem "rails"
|
|
@ -0,0 +1,3 @@
|
|||
language: ruby
|
||||
rvm:
|
||||
- <%= RUBY_VERSION %>
|
|
@ -0,0 +1,4 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in <%=config[:name]%>.gemspec
|
||||
gemspec
|
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) <%=Time.now.year%> <%=config[:author]%>
|
||||
|
||||
MIT License
|
||||
|
||||
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.
|
|
@ -0,0 +1,31 @@
|
|||
# <%=config[:constant_name]%>
|
||||
|
||||
TODO: Write a gem description
|
||||
|
||||
## Installation
|
||||
|
||||
Add this line to your application's Gemfile:
|
||||
|
||||
```ruby
|
||||
gem '<%=config[:name]%>'
|
||||
```
|
||||
|
||||
And then execute:
|
||||
|
||||
$ bundle
|
||||
|
||||
Or install it yourself as:
|
||||
|
||||
$ gem install <%=config[:name]%>
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Write usage instructions here
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork it ( https://github.com/[my-github-username]/<%=config[:name]%>/fork )
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create a new Pull Request
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue