aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore19
-rw-r--r--.rspec1
-rw-r--r--.travis.yml41
-rw-r--r--CHANGELOG.md12
-rw-r--r--CONTRIBUTING.md26
-rw-r--r--DEVELOPMENT.md2
-rw-r--r--ISSUES.md2
-rw-r--r--README.md27
-rw-r--r--Rakefile20
-rw-r--r--bundler.gemspec8
-rw-r--r--lib/bundler.rb11
-rw-r--r--lib/bundler/cli.rb135
-rw-r--r--lib/bundler/cli/clean.rb14
-rw-r--r--lib/bundler/cli/common.rb2
-rw-r--r--lib/bundler/cli/console.rb49
-rw-r--r--lib/bundler/cli/gem.rb94
-rw-r--r--lib/bundler/cli/install.rb1
-rw-r--r--lib/bundler/cli/outdated.rb8
-rw-r--r--lib/bundler/cli/show.rb2
-rw-r--r--lib/bundler/cli/viz.rb2
-rw-r--r--lib/bundler/current_ruby.rb32
-rw-r--r--lib/bundler/dependency.rb9
-rw-r--r--lib/bundler/dsl.rb29
-rw-r--r--lib/bundler/endpoint_specification.rb2
-rw-r--r--lib/bundler/fetcher.rb25
-rw-r--r--lib/bundler/gem_helper.rb26
-rw-r--r--lib/bundler/gem_helpers.rb1
-rw-r--r--lib/bundler/graph.rb5
-rw-r--r--lib/bundler/index.rb30
-rw-r--r--lib/bundler/installer.rb12
-rw-r--r--lib/bundler/lockfile_parser.rb12
-rw-r--r--lib/bundler/parallel_workers.rb7
-rw-r--r--lib/bundler/parallel_workers/thread_worker.rb30
-rw-r--r--lib/bundler/parallel_workers/unix_worker.rb101
-rw-r--r--lib/bundler/parallel_workers/worker.rb69
-rw-r--r--lib/bundler/ruby_version.rb2
-rw-r--r--lib/bundler/rubygems_ext.rb10
-rw-r--r--lib/bundler/s3_fetcher.rb43
-rw-r--r--lib/bundler/source.rb1
-rw-r--r--lib/bundler/source/git.rb4
-rw-r--r--lib/bundler/source/path.rb2
-rw-r--r--lib/bundler/source/rubygems.rb97
-rw-r--r--lib/bundler/source/svn.rb259
-rw-r--r--lib/bundler/source/svn/svn_proxy.rb110
-rw-r--r--lib/bundler/source_list.rb13
-rw-r--r--lib/bundler/templates/newgem/LICENSE.txt.tt2
-rw-r--r--lib/bundler/templates/newgem/Rakefile.tt2
-rw-r--r--lib/bundler/templates/newgem/consolerc.tt3
-rw-r--r--lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt2
-rw-r--r--lib/bundler/templates/newgem/gitignore.tt2
-rw-r--r--lib/bundler/templates/newgem/newgem.gemspec.tt24
-rw-r--r--lib/bundler/worker.rb73
-rw-r--r--man/bundle-config.ronn19
-rw-r--r--man/gemfile.5.ronn95
-rw-r--r--spec/bundler/dsl_spec.rb69
-rw-r--r--spec/bundler/gem_helper_spec.rb30
-rw-r--r--spec/bundler/s3_fetcher_spec.rb29
-rw-r--r--spec/bundler/source/rubygems_spec.rb11
-rw-r--r--spec/bundler/source_list_spec.rb68
-rw-r--r--spec/cache/gems_spec.rb32
-rw-r--r--spec/cache/platform_spec.rb24
-rw-r--r--spec/cache/svn_spec.rb82
-rw-r--r--spec/commands/binstubs_spec.rb40
-rw-r--r--spec/commands/clean_spec.rb62
-rw-r--r--spec/commands/console_spec.rb28
-rw-r--r--spec/commands/exec_spec.rb58
-rw-r--r--spec/commands/licenses_spec.rb13
-rw-r--r--spec/commands/newgem_spec.rb28
-rw-r--r--spec/commands/open_spec.rb12
-rw-r--r--spec/commands/outdated_spec.rb12
-rw-r--r--spec/commands/package_spec.rb14
-rw-r--r--spec/commands/show_spec.rb41
-rw-r--r--spec/install/deploy_spec.rb12
-rw-r--r--spec/install/gemfile/svn_spec.rb582
-rw-r--r--spec/install/gems/dependency_api_spec.rb9
-rw-r--r--spec/install/gems/platform_spec.rb26
-rw-r--r--spec/lock/lockfile_spec.rb28
-rw-r--r--spec/lock/svn_spec.rb35
-rw-r--r--spec/realworld/parallel_spec.rb19
-rw-r--r--spec/spec_helper.rb53
-rw-r--r--spec/support/builders.rb67
-rw-r--r--spec/support/helpers.rb25
-rw-r--r--spec/support/less_than_proc.rb14
-rw-r--r--spec/update/svn_spec.rb100
84 files changed, 2656 insertions, 596 deletions
diff --git a/.gitignore b/.gitignore
index 874a3b8e..4f79ffe3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,23 +3,14 @@
# 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
+/tmp/
-# rubinius bytecode
-*.rbc
-.rbx/
+# gems built by `rake build`
+/pkg/
# output from ronn
-lib/bundler/man/
+/lib/bundler/man/
# output from ci_reporter
-spec/reports/
+/spec/reports/
diff --git a/.rspec b/.rspec
index 8c18f1ab..19a7e9ad 100644
--- a/.rspec
+++ b/.rspec
@@ -1,2 +1,3 @@
--format documentation
--color
+--warnings
diff --git a/.travis.yml b/.travis.yml
index a860fc60..b5cebe82 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,7 @@
language: ruby
script: rake spec:travis
before_script: travis_retry rake spec:travis:deps
+
branches:
only:
- master
@@ -11,39 +12,50 @@ branches:
- 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:
+ slack:
on_success: change
on_failure: always
rooms:
- # Bundler Ops
- secure: MNTSGIySYwHia5gIgRiZxXtPMPDIP9KmzQk7Kq2ZoVvP3mIk8W1TMkvcyFkEf6uCasyVZZixzUBfY+E0BlHAz1ycQpTh1jvSpuIpEVYW48ShJldJ+8W8xfzafyOHii3z7VrDaomEffmMDdmHRsbQAfekMjdR4bTpXtT9V+wOXlg=
+ - secure: JxBi7DDJGkIF/7f/FSN/HUHpvV4EKfQccZHTPd1b2pNJn3GXo6u+tNVbAw2WjxYzPyPQI3ZcYBCU9SEXp/i7VmG8uMzh8Kyildw+miSKYKVb90uYqcsXWzbxwyNBgJLvyDkzST45H5lgnyAicee3WkFes/WDZikIajbH7ztdb04=
+
rvm:
- - 2.1.1
+ - 2.1.2
- 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
+ - RGV=v2.3.0
+
matrix:
+ fast_finish: true
include:
+ # Ruby 2.1, Rubygems 2.2.2 and up
+ - rvm: 2.1.2
+ env: RGV=v2.2.2
# Ruby 2.0.0, Rubygems 2.0 and up
- rvm: 2.0.0
+ env: RGV=v2.2.2
+ - 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.2.2
+ - rvm: 1.9.3
env: RGV=v2.1.11
- rvm: 1.9.3
env: RGV=v2.0.14
@@ -57,7 +69,7 @@ matrix:
env: RGV=v1.5.3
# Ruby 1.8.7, Rubygems 1.3.6 and up
- rvm: 1.8.7
- env: RGV=v2.1.11
+ env: RGV=v2.2.2
- rvm: 1.8.7
env: RGV=v2.0.14
- rvm: 1.8.7
@@ -76,21 +88,26 @@ matrix:
env: RGV=v1.3.6
# ALLOWED FAILURES
+ # For no apparent reason, this often goes over the Travis limit
+ - rvm: 1.8.7
+ env: RGV=v2.1.11
# 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
+ env: RGV=v2.3.0
# 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
+ env: RGV=v2.3.0
# Rubinius, the latest (not maintained, but good to know)
- - rvm: rbx
- env: RGV=v2.2.2
+ - rvm: rbx-2
+ env: RGV=v2.3.0
allow_failures:
- - rvm: ruby-head
+ - rvm: 1.8.7
+ env: RGV=v2.1.11
- rvm: 1.9.2
+ - rvm: ruby-head
- rvm: jruby
- - rvm: rbx
+ - rvm: rbx-2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f349e880..9cceb50b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 1.8.0 (unreleased)
+
+Features:
+
+ - add support for SVN sources (@msnexploder)
+ - add metadata allowed_push_host to new gem template (#3002, @juanitofatas)
+ - adds a `--no-install` flag to `bundle package`
+ - add `bundle viz --without` to exclude gem groups from resulting graph (@fnichol)
+ - add support for private S3 sources (@tryba)
+ - prevent whitespace in gem declarations with clear messaging
+
## 1.7.1 (2014-08-20)
Bugfixes:
@@ -102,6 +113,7 @@ Features:
- `bundle show --verbose` Add gem summary to the output (@lardcanoe)
- `bundle gem GEM --ext` now generates a skeleton for a C extension (@superdealloc)
- Avoid using threequals operator where possible (@as-cii)
+ - Add `bundle update --group` to update specific group (#2731 @banyan)
Documentation:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a2fc0cec..24e0cdaa 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,18 +2,20 @@
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.
+If you'd like to help make Bundler better, you totally rock! Here are some ways you can contribute:
+
+ - by using prerelease versions (run `gem install bundler --pre`)
+ - by [reporting bugs you encounter](https://github.com/bundler/bundler/issues/new)
+ - by [suggesting new features](https://github.com/bundler/bundler-features/issues/new)
+ - by adding to or editing [the Bundler documentation website](http://bundler.io) and [Bundler man pages](http://bundler.io/man/bundle.1.html)
+ - by [checking issues for completeness](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md#bug-triage)
+ - by closing issues that are not complete
+ - by adding a failing test for reproducible [reported bugs](https://github.com/bundler/bundler/issues)
+ - by reviewing [pull requests](https://github.com/bundler/bundler/pulls) and suggesting improvements
+ - by improving existing code, including [suggestions from PullReview](https://www.pullreview.com/github/bundler/bundler/reviews/master)
+ - by [writing code](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md) (no patch is too small! fix typos or bad whitespace)
+
+If you need help getting started, check out the [DEVELOPMENT](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md) file for steps that will get you up and running.
Thanks for helping us make Bundler better.
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index dc52a990..365de702 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -2,7 +2,7 @@ Great to have you here! Here are a few ways you can help out with [Bundler](http
# 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.
+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/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
diff --git a/ISSUES.md b/ISSUES.md
index 560f6508..221d04d2 100644
--- a/ISSUES.md
+++ b/ISSUES.md
@@ -10,7 +10,7 @@ discuss features. The bundler issue tracker is only for bugs.**
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).
+Detailed information about each Bundler command, including help with common problems, can be found in the [Bundler man pages](http://bundler.io/man/bundle.1.html).
## Troubleshooting
diff --git a/README.md b/README.md
index ef5a8fe2..094dfb47 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,12 @@
-[![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)
+[![Version ](https://img.shields.io/gem/v/bundler.svg?style=flat)](https://rubygems.org/gems/bundler)
+[![Build Status](https://img.shields.io/travis/bundler/bundler/master.svg?style=flat)](https://travis-ci.org/bundler/bundler)
+[![Code Climate](https://img.shields.io/codeclimate/github/bundler/bundler.svg?style=flat)](https://codeclimate.com/github/bundler/bundler)
+[![Gittip
+](http://img.shields.io/gittip/bundler.svg?style=flat)](http://gittip.com/bundler)
# Bundler: a gem to bundle gems
-Bundler keeps ruby applications running the same code on every machine.
+
+Bundler makes sure Ruby applications run 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.
@@ -23,18 +26,16 @@ See [bundler.io](http://bundler.io) for the full documentation.
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)
+### Other questions
-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.
+To see what has changed in recent versions of Bundler, see the [CHANGELOG](https://github.com/bundler/bundler/blob/master/CHANGELOG.md).
-### Core Team
+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).
-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)).
+### Contributing
-### Other questions
+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)
-To see what has changed in recent versions of Bundler, see the [CHANGELOG](https://github.com/bundler/bundler/blob/master/CHANGELOG.md).
+### Support work on Bundler
-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).
+Bundler is developed entirely by a team of volunteers. If Bundler saves your company time and money, contribute to the [Bundler development fund on Gittip](http://www.gittip.com/bundler). Every dollar goes towards Bundler documentation, outreach, and development.
diff --git a/Rakefile b/Rakefile
index 3775c3dd..078d0361 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,10 +1,10 @@
# -*- encoding: utf-8 -*-
$:.unshift File.expand_path("../lib", __FILE__)
-require 'rubygems'
require 'shellwords'
require 'benchmark'
RUBYGEMS_REPO = File.expand_path("tmp/rubygems")
+BUNDLER_SPEC = Gem::Specification.load("bundler.gemspec")
def safe_task(&block)
yield
@@ -30,8 +30,7 @@ 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|
+ deps = Hash[BUNDLER_SPEC.development_dependencies.map do |d|
[d.name, d.requirement.to_s]
end]
@@ -65,7 +64,7 @@ namespace :spec do
# https://github.com/rubygems/rubygems/issues/784
sh "gem update --system 2.1.11"
else
- # Downgrade Rubygems so RSpec 3 can be instaled
+ # Downgrade Rubygems so RSpec 3 can be installed
# https://github.com/rubygems/rubygems/issues/813
sh "gem update --system 2.2.0"
end
@@ -76,13 +75,12 @@ namespace :spec do
end
begin
+ rspec = BUNDLER_SPEC.development_dependencies.find{|d| d.name == "rspec" }
+ gem 'rspec', rspec.requirement.to_s
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
+ RSpec::Core::RakeTask.new
task :spec => "man:build"
namespace :spec do
@@ -114,7 +112,7 @@ begin
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)
+ 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 v2.3.0)
(branches + releases).each do |rg|
desc "Run specs with Rubygems #{rg}"
RSpec::Core::RakeTask.new(rg) do |t|
@@ -160,7 +158,9 @@ begin
end
task "setup_co" do
- ENV["RUBYOPT"] = "-I#{File.expand_path ENV['RG']} #{rubyopt}"
+ rg = File.expand_path ENV['RG']
+ puts "Running specs against Rubygems in #{rg}..."
+ ENV["RUBYOPT"] = "-I#{rg} #{rubyopt}"
end
task "co" => "setup_co"
diff --git a/bundler.gemspec b/bundler.gemspec
index dc8fbc8d..bd4d66bb 100644
--- a/bundler.gemspec
+++ b/bundler.gemspec
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
spec.version = Bundler::VERSION
spec.licenses = ['MIT']
spec.authors = ["André Arko", "Terence Lee", "Carl Lerche", "Yehuda Katz"]
- spec.email = ["andre@arko.net"]
+ spec.email = ["andre.arko+terence.lee@gmail.com"]
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}
@@ -21,9 +21,11 @@ Gem::Specification.new do |spec|
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/})
+ # we don't check in man pages, but we need to ship them because
+ # we use them to generate the long-form help for each command.
+ spec.files += Dir.glob('lib/bundler/man/**/*')
+ spec.test_files = spec.files.grep(%r{^spec/})
spec.executables = %w(bundle bundler)
spec.require_paths = ["lib"]
end
diff --git a/lib/bundler.rb b/lib/bundler.rb
index 1eaf194b..6de4afc7 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -37,6 +37,7 @@ module Bundler
autoload :RubyVersion, 'bundler/ruby_version'
autoload :RubyDsl, 'bundler/ruby_dsl'
autoload :Runtime, 'bundler/runtime'
+ autoload :S3Fetcher, 'bundler/s3_fetcher'
autoload :Settings, 'bundler/settings'
autoload :SharedHelpers, 'bundler/shared_helpers'
autoload :SpecSet, 'bundler/spec_set'
@@ -59,6 +60,7 @@ module Bundler
class InstallHookError < BundlerError; status_code(8) ; end
class PathError < BundlerError; status_code(13) ; end
class GitError < BundlerError; status_code(11) ; end
+ class SVNError < BundlerError; status_code(23) ; end
class DeprecatedError < BundlerError; status_code(12) ; end
class GemspecError < BundlerError; status_code(14) ; end
class InvalidOption < BundlerError; status_code(15) ; end
@@ -365,10 +367,19 @@ module Bundler
@git_present = Bundler.which("git") || Bundler.which("git.exe")
end
+ def svn_present?
+ return @svn_present if defined?(@svn_present)
+ @svn_present = Bundler.which("svn") || Bundler.which("svn.exe")
+ end
+
def ruby_version
@ruby_version ||= SystemRubyVersion.new
end
+ def reset!
+ @definition = nil
+ end
+
private
def eval_yaml_gemspec(path, contents)
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index c3e5d8b4..8da526e2 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -4,6 +4,7 @@ require 'bundler/vendored_thor'
module Bundler
class CLI < Thor
include Thor::Actions
+ AUTO_INSTALL_CMDS = %w[show binstubs outdated exec open console licenses clean]
def self.start(*)
super
@@ -12,11 +13,13 @@ module Bundler
raise e
end
- def initialize(*)
+ def initialize(*args)
super
+ current_cmd = args.last[:current_command].name
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)
+ auto_install if AUTO_INSTALL_CMDS.include?(current_cmd)
rescue UnknownArgumentError => e
raise InvalidOption, e.message
ensure
@@ -30,9 +33,9 @@ module Bundler
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"
+ class_option "verbose", :type => :boolean, :banner => "Enable verbose output mode", :aliases => "-V"
def help(cli = nil)
case cli
@@ -82,12 +85,12 @@ module Bundler
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 "dry-run", :type => :boolean, :default => false, :banner =>
+ "Lock the Gemfile"
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
@@ -104,41 +107,41 @@ module Bundler
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 "binstubs", :type => :string, :lazy_default => "bin", :banner =>
+ "Generate bin stubs for bundled gems to ./bin"
+ method_option "clean", :type => :boolean, :banner =>
+ "Run bundle clean automatically after install"
+ method_option "deployment", :type => :boolean, :banner =>
+ "Install using defaults tuned for deployment environments"
+ method_option "frozen", :type => :boolean, :banner =>
+ "Do not allow the Gemfile.lock to be updated after this install"
+ method_option "full-index", :type => :boolean, :banner =>
+ "Use the rubygems modern index instead of the API endpoint"
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 "jobs", :aliases => "-j", :type => :numeric, :banner =>
+ "Specify the number of jobs to run in parallel"
+ method_option "local", :type => :boolean, :banner =>
+ "Do not attempt to fetch gems remotely and use the gem cache instead"
method_option "no-cache", :type => :boolean, :banner =>
"Don't update the existing gem cache."
+ method_option "no-prune", :type => :boolean, :banner =>
+ "Don't remove stale gems from the cache."
+ 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 "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 "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 "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"
+ method_option "without", :type => :array, :banner =>
+ "Exclude gems that are part of the specified named group."
def install
require 'bundler/cli/install'
@@ -151,17 +154,18 @@ module Bundler
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 "full-index", :type => :boolean, :banner =>
+ "Use the rubygems modern index instead of the API endpoint"
+ method_option "group", :aliases => "-g", :type => :array, :banner =>
+ "Update a specific group"
+ method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
+ "Specify the number of jobs to run in parallel"
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"
+ method_option "source", :type => :array, :banner =>
+ "Update a specific source (and all gems associated with it)"
def update(*gems)
require 'bundler/cli/update'
Update.new(options, gems).run
@@ -180,15 +184,15 @@ module Bundler
end
map %w(list) => "show"
- desc "binstubs GEM [OPTIONS]", "install the binstubs of the listed gem"
+ 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"
+ "Overwrite existing binstubs if they exist"
+ method_option "path", :type => :string, :lazy_default => "bin", :banner =>
+ "Binstub destination directory (default bin)"
def binstubs(*gems)
require 'bundler/cli/binstubs'
Binstubs.new(options, gems).run
@@ -201,10 +205,10 @@ module Bundler
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 "pre", :type => :boolean, :banner => "Check for newer pre-release gems"
+ method_option "source", :type => :array, :banner => "Check against a specific source"
method_option "strict", :type => :boolean, :banner =>
"Only list newer versions allowed by your Gemfile requirements"
def outdated(*gems)
@@ -213,20 +217,21 @@ module Bundler
end
desc "cache [OPTIONS]", "Cache all the gems to vendor/cache", :hide => true
+ method_option "all", :type => :boolean, :banner => "Include all sources (including path, git and svn)."
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 "all", :type => :boolean, :banner => "Include all sources (including path, git and svn)."
+ method_option "gemfile", :type => :string, :banner => "Use the specified gemfile instead of Gemfile"
+ method_option "no-install", :type => :boolean, :banner => "Don't actually install the gems, just package."
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"
+ method_option "quiet", :type => :boolean, :banner => "Only output warnings and errors."
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
@@ -274,16 +279,10 @@ module Bundler
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
+ Console.new(options, group).run
end
desc "version", "Prints the bundler's version information"
@@ -313,9 +312,11 @@ module Bundler
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 ..."
+ method_option :requirements, :type => :boolean, :default => false, :aliases => '-r', :banner => "Set to show the version of each required dependency."
+ method_option :version, :type => :boolean, :default => false, :aliases => '-v', :banner => "Set to show each gem version."
+ method_option :without, :type => :array, :default => [], :banner => "Exclude gems that are part of the specified named group."
+
def viz
require 'bundler/cli/viz'
Viz.new(options).run
@@ -323,12 +324,13 @@ module Bundler
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"
+ method_option :ext, :type => :boolean, :default => false, :banner => "Generate the boilerplate for C extension code"
+ 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."
def gem(name)
require 'bundler/cli/gem'
@@ -341,9 +343,9 @@ module Bundler
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"
+ "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"
+ "Forces clean even if --path is not set"
def clean
require 'bundler/cli/clean'
Clean.new(options.dup).run
@@ -368,5 +370,26 @@ module Bundler
Env.new.write($stdout)
end
+ private
+
+ # Automatically invoke `bundle install` and resume if
+ # Bundler.settings[:auto_install] exists. This is set through config cmd
+ # `bundle config auto_install 1`.
+ #
+ # Note that this method `nil`s out the global Definition object, so it
+ # should be called first, before you instantiate anything like an
+ # `Installer` that'll keep a reference to the old one instead.
+ def auto_install
+ return unless Bundler.settings[:auto_install]
+
+ begin
+ Bundler.definition.specs
+ rescue GemNotFound
+ Bundler.ui.info "Automatically installing missing gems."
+ Bundler.reset!
+ invoke :install, []
+ Bundler.reset!
+ end
+ end
end
end
diff --git a/lib/bundler/cli/clean.rb b/lib/bundler/cli/clean.rb
index e7a4c014..8e86e916 100644
--- a/lib/bundler/cli/clean.rb
+++ b/lib/bundler/cli/clean.rb
@@ -7,10 +7,16 @@ module Bundler
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"
+ reqire_path_or_force
+ Bundler.load.clean(options[:"dry-run"])
+ end
+
+ protected
+
+ def reqire_path_or_force
+ if !Bundler.settings[:path] && !options[:force]
+ Bundler.ui.error "Cleaning all the gems on your system is dangerous! " \
+ "To remove every gem not in this bundle, run `bundle clean --force`."
exit 1
end
end
diff --git a/lib/bundler/cli/common.rb b/lib/bundler/cli/common.rb
index 0548525a..83315a27 100644
--- a/lib/bundler/cli/common.rb
+++ b/lib/bundler/cli/common.rb
@@ -25,6 +25,8 @@ module Bundler
else
ask_for_spec_from(specs)
end
+ rescue RegexpError
+ raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
end
def self.ask_for_spec_from(specs)
diff --git a/lib/bundler/cli/console.rb b/lib/bundler/cli/console.rb
index bf000936..f52bfb11 100644
--- a/lib/bundler/cli/console.rb
+++ b/lib/bundler/cli/console.rb
@@ -1,41 +1,38 @@
module Bundler
class CLI::Console
- attr_reader :options, :group, :consoles
- def initialize(options, group, consoles)
+ attr_reader :options, :group
+ def initialize(options, group)
@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 = get_console(Bundler.settings[:console] || 'irb')
+ load '.consolerc' if File.exists?('.consolerc')
+ console.start
+ end
- 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
+ def get_console(name)
+ require name
+ get_constant(name)
+ rescue LoadError
+ Bundler.ui.error "Couldn't load console #{name}"
+ get_constant('irb')
+ end
- console.start
+ def get_constant(name)
+ const_name = {
+ 'pry' => :Pry,
+ 'ripl' => :Ripl,
+ 'irb' => :IRB,
+ }[name]
+ Object.const_get(const_name)
+ rescue NameError
+ Bundler.ui.error "Could not find constant #{const_name}"
+ exit 1
end
end
diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb
index 36663419..485721c5 100644
--- a/lib/bundler/cli/gem.rb
+++ b/lib/bundler/cli/gem.rb
@@ -1,34 +1,34 @@
+require 'pathname'
+
module Bundler
class CLI::Gem
- attr_reader :options, :gem_name, :thor
+ attr_reader :options, :gem_name, :thor, :name, :target
+
def initialize(options, gem_name, thor)
@options = options
@gem_name = gem_name
@thor = thor
+
+ @name = gem_name.chomp("/") # remove trailing slash if present
+ @target = Pathname.pwd.join(name)
+
+ validate_ext_name if options[:ext]
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,
+ :makefile_path => "#{underscored_name}/#{underscored_name}",
:constant_name => constant_name,
:constant_array => constant_array,
:author => git_user_name.empty? ? "TODO: Write your name" : git_user_name,
@@ -36,42 +36,66 @@ module Bundler
: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
+
+ templates = {
+ "Gemfile.tt" => "Gemfile",
+ "gitignore.tt" => ".gitignore",
+ "lib/newgem.rb.tt" => "lib/#{namespaced_path}.rb",
+ "lib/newgem/version.rb.tt" => "lib/#{namespaced_path}/version.rb",
+ "LICENSE.txt.tt" => "LICENSE.txt",
+ "newgem.gemspec.tt" => "#{name}.gemspec",
+ "consolerc.tt" => ".consolerc",
+ "Rakefile.tt" => "Rakefile",
+ "README.md.tt" => "README.md"
+ }
+
+ templates.merge!("bin/newgem.tt" => "bin/#{name}") if options[:bin]
+ templates.merge!(".travis.yml.tt" => ".travis.yml") if options[:test]
+
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)
+ templates.merge!(
+ "rspec.tt" => ".rspec",
+ "spec/spec_helper.rb.tt" => "spec/spec_helper.rb",
+ "spec/newgem_spec.rb.tt" => "spec/#{namespaced_path}_spec.rb"
+ )
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)
+ templates.merge!(
+ "test/minitest_helper.rb.tt" => "test/minitest_helper.rb",
+ "test/test_newgem.rb.tt" => "test/test_#{namespaced_path}.rb"
+ )
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)
+ templates.merge!(
+ "ext/newgem/extconf.rb.tt" => "ext/#{name}/extconf.rb",
+ "ext/newgem/newgem.h.tt" => "ext/#{name}/#{underscored_name}.h",
+ "ext/newgem/newgem.c.tt" => "ext/#{name}/#{underscored_name}.c"
+ )
+ end
+
+ templates.each do |src, dst|
+ thor.template("newgem/#{src}", target.join(dst), 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
+ # Open gemspec in editor
+ thor.run("#{options["edit"]} \"#{target.join("#{name}.gemspec")}\"")
end
end
+ def validate_ext_name
+ return unless 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
+
end
end
diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb
index 9bb7f681..bd808055 100644
--- a/lib/bundler/cli/install.rb
+++ b/lib/bundler/cli/install.rb
@@ -64,6 +64,7 @@ module Bundler
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[:no_install] = true if options["no-install"]
Bundler.settings[:clean] = options["clean"] if options["clean"]
Bundler.settings.without = options[:without]
Bundler.ui.level = "warn" if options[:quiet]
diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb
index ee6387ac..22c2f377 100644
--- a/lib/bundler/cli/outdated.rb
+++ b/lib/bundler/cli/outdated.rb
@@ -50,8 +50,8 @@ module Bundler
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
+ scm_outdated = current_spec.scm_version != active_spec.scm_version
+ if gem_outdated || scm_outdated
if out_count == 0
if options["pre"]
Bundler.ui.info "Outdated gems included in the bundle (including pre-releases):"
@@ -60,8 +60,8 @@ module Bundler
end
end
- spec_version = "#{active_spec.version}#{active_spec.git_version}"
- current_version = "#{current_spec.version}#{current_spec.git_version}"
+ spec_version = "#{active_spec.version}#{active_spec.scm_version}"
+ current_version = "#{current_spec.version}#{current_spec.scm_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
diff --git a/lib/bundler/cli/show.rb b/lib/bundler/cli/show.rb
index 07ff63dc..fa4c12ad 100644
--- a/lib/bundler/cli/show.rb
+++ b/lib/bundler/cli/show.rb
@@ -35,7 +35,7 @@ module Bundler
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})"
+ desc = " * #{s.name} (#{s.version}#{s.scm_version})"
if @options[:verbose]
Bundler.ui.info "#{desc} - #{s.summary || 'No description available.'}"
else
diff --git a/lib/bundler/cli/viz.rb b/lib/bundler/cli/viz.rb
index c404ddbb..09557e59 100644
--- a/lib/bundler/cli/viz.rb
+++ b/lib/bundler/cli/viz.rb
@@ -8,7 +8,7 @@ module Bundler
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 = Graph.new(Bundler.load, output_file, options[:version], options[:requirements], options[:format], options[:without])
graph.viz
rescue LoadError => e
Bundler.ui.error e.inspect
diff --git a/lib/bundler/current_ruby.rb b/lib/bundler/current_ruby.rb
index 8d012a03..648eb75e 100644
--- a/lib/bundler/current_ruby.rb
+++ b/lib/bundler/current_ruby.rb
@@ -87,6 +87,38 @@ module Bundler
Bundler::WINDOWS
end
+ def mswin_18?
+ mswin? && on_18?
+ end
+
+ def mswin_19?
+ mswin? && on_19?
+ end
+
+ def mswin_20?
+ mswin? && on_20?
+ end
+
+ def mswin_21?
+ mswin? && on_21?
+ end
+
+ def mswin64?
+ Bundler::WINDOWS && Gem::Platform.local.os == "mswin64" && Gem::Platform.local.cpu == 'x64'
+ end
+
+ def mswin64_19?
+ mswin64? && on_19?
+ end
+
+ def mswin64_20?
+ mswin64? && on_20?
+ end
+
+ def mswin64_21?
+ mswin64? && on_21?
+ end
+
def mingw?
Bundler::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu != 'x64'
end
diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb
index c0e8f8bf..de2bee9a 100644
--- a/lib/bundler/dependency.rb
+++ b/lib/bundler/dependency.rb
@@ -24,6 +24,14 @@ module Bundler
:jruby_18 => Gem::Platform::JAVA,
:jruby_19 => Gem::Platform::JAVA,
:mswin => Gem::Platform::MSWIN,
+ :mswin_18 => Gem::Platform::MSWIN,
+ :mswin_19 => Gem::Platform::MSWIN,
+ :mswin_20 => Gem::Platform::MSWIN,
+ :mswin_21 => Gem::Platform::MSWIN,
+ :mswin64 => Gem::Platform::MSWIN64,
+ :mswin64_19 => Gem::Platform::MSWIN64,
+ :mswin64_20 => Gem::Platform::MSWIN64,
+ :mswin64_21 => Gem::Platform::MSWIN64,
:mingw => Gem::Platform::MINGW,
:mingw_18 => Gem::Platform::MINGW,
:mingw_19 => Gem::Platform::MINGW,
@@ -89,7 +97,6 @@ module Bundler
out << "\n"
end
-
def specific?
super
rescue NoMethodError
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index ef5c6fba..5f27f872 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -24,7 +24,7 @@ module Bundler
@platforms = []
@env = nil
@ruby_version = nil
- add_github_sources
+ add_git_sources
end
def eval_gemfile(gemfile, contents = nil)
@@ -148,6 +148,18 @@ module Bundler
with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk)
end
+ def svn(uri, options = {}, source_options = {}, &blk)
+ unless block_given?
+ msg = "You can no longer specify a svn source by itself. Instead, \n" \
+ "either use the :svn option on a gem, or specify the gems that \n" \
+ "bundler should find in the svn source by passing a block to \n" \
+ "the svn method."
+ raise DeprecatedError, msg
+ end
+
+ with_source(@sources.add_svn_source(normalize_hash(options).merge("uri" => uri)), &blk)
+ end
+
def to_definition(lockfile, unlock)
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version)
end
@@ -182,13 +194,19 @@ module Bundler
private
- def add_github_sources
+ def add_git_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" }
+
+ git_source(:bitbucket) do |repo_name|
+ user_name, repo_name = repo_name.split '/'
+ repo_name ||= user_name
+ "https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git"
+ end
end
def with_source(source)
@@ -209,13 +227,16 @@ module Bundler
end
def valid_keys
- @valid_keys ||= %w(group groups git path name branch ref tag require submodules platform platforms type source)
+ @valid_keys ||= %w(group groups git svn 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
+ if name =~ /\s/
+ raise GemfileError, %{'#{name}' is not a valid gem name because it contains whitespace.}
+ end
normalize_hash(opts)
@@ -259,7 +280,7 @@ module Bundler
opts["git"] = @git_sources[git_name].call(opts[git_name])
end
- ["git", "path"].each do |type|
+ ["git", "path", "svn"].each do |type|
if param = opts[type]
if version.first && version.first =~ /^\s*=?\s*(\d[^\s]*)\s*$/
options = opts.merge("name" => name, "version" => $1)
diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb
index 8566f265..71d5e115 100644
--- a/lib/bundler/endpoint_specification.rb
+++ b/lib/bundler/endpoint_specification.rb
@@ -18,7 +18,7 @@ module Bundler
end
# needed for standalone, load required_paths from local gemspec
- # after the gem in installed
+ # after the gem is installed
def require_paths
if @remote_specification
@remote_specification.require_paths
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
index 8c8c8689..0512c768 100644
--- a/lib/bundler/fetcher.rb
+++ b/lib/bundler/fetcher.rb
@@ -98,6 +98,7 @@ module Bundler
@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
+ # TODO: pull auth from config (like #retry_with_auth does) here
Socket.do_not_reverse_lookup = true
connection # create persistent connection
@@ -241,6 +242,15 @@ module Bundler
"#<#{self.class}:0x#{object_id} uri=#{uri}>"
end
+ protected
+ def add_basic_auth(req)
+ if @remote_uri.user
+ user = CGI.unescape(@remote_uri.user)
+ password = @remote_uri.password ? CGI.unescape(@remote_uri.password) : nil
+ req.basic_auth(user, password)
+ end
+ end
+
private
HTTP_ERRORS = [
@@ -276,14 +286,15 @@ module Bundler
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)
+ add_basic_auth(req)
+ result = connection.request(uri, req)
+
+ case result
+ when Net::HTTPUnauthorized, Net::HTTPForbidden
+ retry_with_auth { request(uri) }
+ else
+ result
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
diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb
index 6b7a1760..6db31901 100644
--- a/lib/bundler/gem_helper.rb
+++ b/lib/bundler/gem_helper.rb
@@ -44,9 +44,22 @@ module Bundler
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)
+ desc "Create tag #{version_tag} and build and push #{name}-#{version}.gem to Rubygems\n" \
+ "To prevent publishing in Rubygems use `gem_push=no rake release`"
+ task 'release' => ['build', 'release:guard_clean',
+ 'release:source_control_push', 'release:rubygem_push'] do
+ end
+
+ task 'release:guard_clean' do
+ guard_clean
+ end
+
+ task 'release:source_control_push' do
+ tag_version { git_push } unless already_tagged?
+ end
+
+ task 'release:rubygem_push' do
+ rubygem_push(built_gem_path) if gem_push?
end
GemHelper.instance = self
@@ -70,13 +83,6 @@ module Bundler
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?
diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb
index 2040604b..a3bb5149 100644
--- a/lib/bundler/gem_helpers.rb
+++ b/lib/bundler/gem_helpers.rb
@@ -5,6 +5,7 @@ module Bundler
GENERICS = [
[Gem::Platform.new('java'), Gem::Platform.new('java')],
[Gem::Platform.new('mswin32'), Gem::Platform.new('mswin32')],
+ [Gem::Platform.new('mswin64'), Gem::Platform.new('mswin64')],
[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')]
diff --git a/lib/bundler/graph.rb b/lib/bundler/graph.rb
index 92f538b9..0b5c5723 100644
--- a/lib/bundler/graph.rb
+++ b/lib/bundler/graph.rb
@@ -3,12 +3,13 @@ module Bundler
class Graph
GRAPH_NAME = :Gemfile
- def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png")
+ def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png", without = [])
@env = env
@output_file = output_file
@show_version = show_version
@show_requirements = show_requirements
@output_format = output_format
+ @without_groups = without.map(&:to_sym)
@groups = []
@relations = Hash.new {|h, k| h[k] = Set.new}
@@ -53,6 +54,8 @@ module Bundler
relations = Hash.new {|h, k| h[k] = Set.new}
@env.current_dependencies.each do |dependency|
dependency.groups.each do |group|
+ next if @without_groups.include?(group)
+
relations[group.to_s].add(dependency)
@relations[group.to_s].add(dependency.name)
diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb
index 104bf444..9d2966b7 100644
--- a/lib/bundler/index.rb
+++ b/lib/bundler/index.rb
@@ -16,7 +16,7 @@ module Bundler
def initialize
@sources = []
@cache = {}
- @specs = Hash.new { |h,k| h[k] = [] }
+ @specs = Hash.new { |h,k| h[k] = Hash.new }
@all_specs = Hash.new { |h,k| h[k] = [] }
end
@@ -24,11 +24,11 @@ module Bundler
super
@sources = @sources.dup
@cache = {}
- @specs = Hash.new { |h,k| h[k] = [] }
+ @specs = Hash.new { |h,k| h[k] = Hash.new }
@all_specs = Hash.new { |h,k| h[k] = [] }
- o.specs.each do |name, array|
- @specs[name] = array.dup
+ o.specs.each do |name, hash|
+ @specs[name] = hash.dup
end
o.all_specs.each do |name, array|
@all_specs[name] = array.dup
@@ -88,19 +88,14 @@ module Bundler
alias [] search
def <<(spec)
- arr = specs_by_name(spec.name)
+ @specs[spec.name]["#{spec.version}-#{spec.platform}"] = spec
- 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)
+ specs.values.each do |spec_sets|
+ spec_sets.values.each(&blk)
end
end
@@ -119,9 +114,9 @@ module Bundler
if (dupes = search_by_spec(s)) && dupes.any?
@all_specs[s.name] = [s] + dupes
next unless override_dupes
- @specs[s.name] -= dupes
+ self << s
end
- @specs[s.name] << s
+ self << s
end
self
end
@@ -151,7 +146,7 @@ module Bundler
private
def specs_by_name(name)
- @specs[name]
+ @specs[name].values
end
def search_by_dependency(dependency, base = nil)
@@ -178,9 +173,8 @@ module Bundler
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
+ spec = @specs[spec.name]["#{spec.version}-#{spec.platform}"]
+ spec ? [spec] : []
end
if RUBY_VERSION < '1.9'
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index ed743d40..403a422a 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -1,6 +1,6 @@
require 'erb'
require 'rubygems/dependency_installer'
-require 'bundler/parallel_workers'
+require 'bundler/worker'
module Bundler
class Installer < Environment
@@ -84,7 +84,7 @@ module Bundler
# 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?
+ if jobs > 1 && can_install_in_parallel?
install_in_parallel jobs, options[:standalone]
else
install_sequentially options[:standalone]
@@ -191,7 +191,7 @@ module Bundler
private
- def can_install_parallely?
+ def can_install_in_parallel?
min_rubygems = "2.0.7"
if Bundler.current_ruby.mri? || Bundler.rubygems.provides?(">= #{min_rubygems}")
true
@@ -251,7 +251,7 @@ module Bundler
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}")}
+ file.puts %{$:.unshift "\#{path}/#{path}"}
end
end
end
@@ -274,9 +274,9 @@ module Bundler
remains[spec.name] = true
end
- worker_pool = ParallelWorkers.worker_pool size, lambda { |name, worker|
+ worker_pool = Worker.new size, lambda { |name, worker_num|
spec = name2spec[name]
- message = install_gem_from_spec spec, standalone, worker
+ message = install_gem_from_spec spec, standalone, worker_num
{ :name => spec.name, :post_install => message }
}
diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb
index 32eb43a4..0511f0fc 100644
--- a/lib/bundler/lockfile_parser.rb
+++ b/lib/bundler/lockfile_parser.rb
@@ -19,6 +19,7 @@ module Bundler
GIT = "GIT"
GEM = "GEM"
PATH = "PATH"
+ SVN = "SVN"
SPECS = " specs:"
OPTIONS = /^ ([a-z]+): (.*)$/i
@@ -54,12 +55,13 @@ module Bundler
TYPES = {
"GIT" => Bundler::Source::Git,
"GEM" => Bundler::Source::Rubygems,
- "PATH" => Bundler::Source::Path
+ "PATH" => Bundler::Source::Path,
+ "SVN" => Bundler::Source::SVN
}
def parse_source(line)
case line
- when GIT, GEM, PATH
+ when GIT, GEM, PATH, SVN
@current_source = nil
@opts, @type = {}, line
when SPECS
@@ -67,10 +69,10 @@ module Bundler
when "PATH"
@current_source = TYPES[@type].from_lock(@opts)
@sources << @current_source
- when "GIT"
+ when "GIT", "SVN"
@current_source = TYPES[@type].from_lock(@opts)
- # Strip out duplicate GIT sections
- if @type == "GIT" && @sources.include?(@current_source)
+ # Strip out duplicate GIT / SVN sections
+ if @sources.include?(@current_source)
@current_source = @sources.find { |s| s == @current_source }
else
@sources << @current_source
diff --git a/lib/bundler/parallel_workers.rb b/lib/bundler/parallel_workers.rb
index b28f6304..b95b744b 100644
--- a/lib/bundler/parallel_workers.rb
+++ b/lib/bundler/parallel_workers.rb
@@ -4,15 +4,10 @@ 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
+ ThreadWorker.new(size, job)
end
end
end
diff --git a/lib/bundler/parallel_workers/thread_worker.rb b/lib/bundler/parallel_workers/thread_worker.rb
deleted file mode 100644
index ef2c9ecd..00000000
--- a/lib/bundler/parallel_workers/thread_worker.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-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
diff --git a/lib/bundler/parallel_workers/unix_worker.rb b/lib/bundler/parallel_workers/unix_worker.rb
deleted file mode 100644
index 94a07bf9..00000000
--- a/lib/bundler/parallel_workers/unix_worker.rb
+++ /dev/null
@@ -1,101 +0,0 @@
-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
diff --git a/lib/bundler/parallel_workers/worker.rb b/lib/bundler/parallel_workers/worker.rb
deleted file mode 100644
index 0e101d1c..00000000
--- a/lib/bundler/parallel_workers/worker.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-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
diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb
index 862dd35a..da6ebae0 100644
--- a/lib/bundler/ruby_version.rb
+++ b/lib/bundler/ruby_version.rb
@@ -38,7 +38,7 @@ module Bundler
patchlevel == other.patchlevel
end
- # Returns a tuple of thsee things:
+ # Returns a tuple of these things:
# [diff, this, other]
# The priority of attributes are
# 1. engine
diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb
index dccb6fb2..8b8a1fe6 100644
--- a/lib/bundler/rubygems_ext.rb
+++ b/lib/bundler/rubygems_ext.rb
@@ -64,9 +64,12 @@ module Gem
@groups ||= []
end
- def git_version
- return unless loaded_from && source.is_a?(Bundler::Source::Git)
- " #{source.revision[0..6]}"
+ def scm_version
+ return unless loaded_from
+ case source
+ when Bundler::Source::Git then " #{source.revision[0..6]}"
+ when Bundler::Source::SVN then " #{source.revision}"
+ end
end
def to_gemfile(path = nil)
@@ -147,6 +150,7 @@ module Gem
class Platform
JAVA = Gem::Platform.new('java') unless defined?(JAVA)
MSWIN = Gem::Platform.new('mswin32') unless defined?(MSWIN)
+ MSWIN64 = Gem::Platform.new('mswin64') unless defined?(MSWIN64)
MINGW = Gem::Platform.new('x86-mingw32') unless defined?(MINGW)
X64_MINGW = Gem::Platform.new('x64-mingw32') unless defined?(X64_MINGW)
diff --git a/lib/bundler/s3_fetcher.rb b/lib/bundler/s3_fetcher.rb
new file mode 100644
index 00000000..38026436
--- /dev/null
+++ b/lib/bundler/s3_fetcher.rb
@@ -0,0 +1,43 @@
+require 'base64'
+require 'openssl'
+
+module Bundler
+ class S3Fetcher < Fetcher
+
+ def fetch(uri, counter = 0)
+ super(sign(uri), counter)
+ end
+
+ # Instead of taking a dependency on aws-sdk, use a method modeled on
+ # the signing method in https://github.com/rubygems/rubygems/pull/856
+ def sign(uri, expiration = default_expiration)
+ uri = uri.dup
+ unless uri.user && uri.password
+ raise AuthenticationRequiredError.new("credentials needed in s3 source, like s3://key:secret@bucket-name/")
+ end
+
+ payload = "GET\n\n\n#{expiration}\n/#{uri.host}#{uri.path}"
+ digest = OpenSSL::HMAC.digest('sha1', uri.password, payload)
+ # URI.escape is deprecated, and there isn't yet a replacement that does quite what we want
+ signature = Base64.encode64(digest).gsub("\n", '').gsub(/[\+\/=]/) { |c| BASE64_URI_TRANSLATE[c] }
+ uri.query = [uri.query, "AWSAccessKeyId=#{uri.user}&Expires=#{expiration}&Signature=#{signature}"].compact.join('&')
+ uri.user = nil
+ uri.password = nil
+ uri.scheme = "https"
+ uri.host = [uri.host, "s3.amazonaws.com"].join('.')
+
+ URI.parse(uri.to_s)
+ end
+
+ def default_expiration
+ (Time.now + 3600).to_i # one hour from now
+ end
+
+ BASE64_URI_TRANSLATE = { '+' => '%2B', '/' => '%2F', '=' => '%3D' }.freeze
+ protected
+ # The s3 fetcher does not use the username and password for basic auth,
+ # so this is a no-op
+ def add_basic_auth(req)
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb
index 04643b49..d37a6046 100644
--- a/lib/bundler/source.rb
+++ b/lib/bundler/source.rb
@@ -3,6 +3,7 @@ module Bundler
autoload :Rubygems, 'bundler/source/rubygems'
autoload :Path, 'bundler/source/path'
autoload :Git, 'bundler/source/git'
+ autoload :SVN, 'bundler/source/svn'
def self.mirror_for(uri)
uri = URI(uri.to_s) unless uri.is_a?(URI)
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index 1f720f53..a044f58a 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -46,6 +46,10 @@ module Bundler
out << " specs:\n"
end
+ def hash
+ [self.class, uri, ref, branch, name, version, submodules].hash
+ end
+
def eql?(o)
o.is_a?(Git) &&
uri == o.uri &&
diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb
index c80ac61f..297c7e33 100644
--- a/lib/bundler/source/path.rb
+++ b/lib/bundler/source/path.rb
@@ -54,7 +54,7 @@ module Bundler
end
def hash
- self.class.hash
+ [self.class, expand(path), version].hash
end
def eql?(o)
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index 635bb993..239a5194 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -6,6 +6,7 @@ module Bundler
class Source
class Rubygems < Source
API_REQUEST_LIMIT = 100 # threshold for switching back to the modern index instead of fetching every spec
+ S3_SCHEME = 's3'
attr_reader :remotes, :caches
@@ -93,49 +94,50 @@ module Bundler
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
+ unless Bundler.settings[:no_install]
+ 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
+ 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))
+ # 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
- 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}/"
+ spec.executables.each do |exe|
+ Bundler.mkdir_p Bundler.system_bindir
+ Bundler.sudo "cp -R #{install_path}/bin/#{exe} #{Bundler.system_bindir}/"
+ end
end
+ installed_spec.loaded_from = loaded_from(spec)
end
-
- spec.loaded_from = "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
- installed_spec.loaded_from = spec.loaded_from
+ spec.loaded_from = loaded_from(spec)
["Installing #{version_message(spec)}", spec.post_install_message]
ensure
if install_path && Bundler.requires_sudo?
@@ -186,6 +188,17 @@ module Bundler
end
end
+ def fetchers
+ @fetchers ||= remotes.map do |uri|
+ case uri.scheme
+ when S3_SCHEME
+ Bundler::S3Fetcher.new(uri)
+ else
+ Bundler::Fetcher.new(uri)
+ end
+ end
+ end
+
protected
def source_uris_for_spec(spec)
@@ -194,6 +207,10 @@ module Bundler
private
+ def loaded_from(spec)
+ "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
+ end
+
def cached_gem(spec)
cached_gem = cached_path(spec)
unless cached_gem
@@ -270,12 +287,6 @@ module Bundler
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
diff --git a/lib/bundler/source/svn.rb b/lib/bundler/source/svn.rb
new file mode 100644
index 00000000..a7a42ecf
--- /dev/null
+++ b/lib/bundler/source/svn.rb
@@ -0,0 +1,259 @@
+require 'fileutils'
+require 'uri'
+require 'digest/sha1'
+
+module Bundler
+ class Source
+
+ class SVN < Path
+ autoload :SVNProxy, 'bundler/source/svn/svn_proxy'
+
+ attr_reader :uri, :ref, :options
+
+ 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 revision).each{|k| options[k] = options[k].to_s if options[k] }
+
+ @uri = options["uri"]
+ @ref = options["ref"] || 'HEAD'
+ @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 = "SVN\n"
+ out << " remote: #{@uri}\n"
+ out << " revision: #{revision}\n"
+ out << " ref: #{ref}\n"
+ out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
+ out << " specs:\n"
+ end
+
+ def hash
+ [self.class, uri, ref, name, version].hash
+ end
+
+ def eql?(o)
+ o.is_a?(SVN) &&
+ uri == o.uri &&
+ ref == o.ref &&
+ name == o.name &&
+ version == o.version
+ end
+
+ alias == eql?
+
+ def to_s
+ at = if local?
+ path
+ elsif options["ref"]
+ options["ref"]
+ else
+ ref
+ end
+ "#{uri} (at #{at})"
+ end
+
+ def name
+ File.basename(@uri, '.svn')
+ end
+
+ # This is the path which is going to contain a specific
+ # checkout of the svn repository. When using local svn
+ # repos, this is set to the local repo.
+ def install_path
+ @install_path ||= begin
+ svn_scope = "#{base_name}-#{revision}"
+
+ if Bundler.requires_sudo?
+ Bundler.user_bundle_path.join(Bundler.ruby_scope).join(svn_scope)
+ else
+ Bundler.install_path.join(svn_scope)
+ end
+ end
+ end
+
+ alias :path :install_path
+
+ def extension_dir_name
+ "#{base_name}-#{revision}"
+ end
+
+ def unlock!
+ svn_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 path.exist?
+ raise SVNError, "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 svn proxy without the cached revision
+ # so the Gemfile.lock always picks up the new revision.
+ @svn_proxy = SVNProxy.new(path, uri, ref)
+ true
+ end
+
+ # TODO: actually cache svn specs
+ def specs(*)
+ if has_app_cache? && !local?
+ set_local!(app_cache_path)
+ end
+
+ if requires_checkout? && !@copied
+ svn_proxy.checkout
+ svn_proxy.copy_to(install_path)
+ 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}"
+ svn_proxy.copy_to(install_path)
+ 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)
+ svn_proxy.checkout if requires_checkout?
+ svn_proxy.copy_to(app_cache_path)
+ serialize_gemspecs_in(app_cache_path)
+ end
+
+ def load_spec_files
+ super
+ rescue PathError => e
+ Bundler.ui.trace e
+ raise SVNError, "#{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 svn repository. When using the same svn repository
+ # across different projects, this cache will be shared.
+ # When using local svn repos, this is set to the local repo.
+ def cache_path
+ @cache_path ||= begin
+ svn_scope = "#{base_name}-#{uri_hash}"
+
+ if Bundler.requires_sudo?
+ Bundler.user_bundle_path.join("cache/svn", svn_scope)
+ else
+ Bundler.cache.join("svn", svn_scope)
+ end
+ end
+ end
+
+ def app_cache_dirname
+ "#{base_name}-#{(cached_revision || revision)}"
+ end
+
+ def revision
+ svn_proxy.revision
+ end
+
+ def allow_svn_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 svn might require svn 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 = @svn_proxy = nil
+ @cache_path = @install_path = path
+ end
+
+ def has_app_cache?
+ cached_revision && super
+ end
+
+ def local?
+ @local
+ end
+
+ def requires_checkout?
+ allow_svn_ops? && !local?
+ end
+
+ def base_name
+ File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*},''),".svn")
+ 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/svn URI
+ input = uri
+ end
+ Digest::SHA1.hexdigest(input)
+ end
+
+ def cached_revision
+ options["revision"]
+ end
+
+ def cached?
+ cache_path.exist?
+ end
+
+ def svn_proxy
+ @svn_proxy ||= SVNProxy.new(cache_path, uri, ref, cached_revision, self)
+ end
+
+ end
+
+ end
+end
diff --git a/lib/bundler/source/svn/svn_proxy.rb b/lib/bundler/source/svn/svn_proxy.rb
new file mode 100644
index 00000000..c67a9372
--- /dev/null
+++ b/lib/bundler/source/svn/svn_proxy.rb
@@ -0,0 +1,110 @@
+module Bundler
+ class Source
+ class SVN < Path
+
+ class SVNNotInstalledError < SVNError
+ def initialize
+ msg = "You need to install svn to be able to use gems from svn repositories. "
+ msg << "For help installing svn, please refer to SVNook's tutorial at http://svnbook.red-bean.com/en/1.7/svn.intro.install.html"
+ super msg
+ end
+ end
+
+ class SVNNotAllowedError < SVNError
+ def initialize(command)
+ msg = "Bundler is trying to run a `svn #{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 SVNCommandError < SVNError
+ def initialize(command, path = nil)
+ msg = "SVN error: command `svn #{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 SVNProxy is responsible to interact with svn repositories.
+ # All actions required by the SVN source is encapsulated in this
+ # object.
+ class SVNProxy
+ attr_accessor :path, :uri, :ref
+ attr_writer :revision
+
+ def initialize(path, uri, ref, revision = nil, svn = nil)
+ @path = path
+ @uri = uri
+ @ref = ref
+ @revision = revision
+ @svn = svn
+ raise SVNNotInstalledError.new if allow? && !Bundler.svn_present?
+ end
+
+ def revision
+ @revision ||= svn("info --revision #{ref} #{uri_escaped} | grep \"Revision\" | awk '{print $2}'").strip
+ end
+
+ def checkout
+ if path.exist?
+ Bundler.ui.confirm "Updating #{uri}"
+ in_path do
+ svn_retry %|update --force --quiet --revision #{revision}|
+ end
+ else
+ Bundler.ui.info "Fetching #{uri}"
+ FileUtils.mkdir_p(path.dirname)
+ svn_retry %|checkout --revision #{revision} #{uri_escaped} "#{path}"|
+ end
+ end
+
+ def copy_to(destination)
+ FileUtils.mkdir_p(destination.dirname)
+ FileUtils.rm_rf(destination)
+ FileUtils.cp_r(path, destination)
+ File.chmod((0777 & ~File.umask), destination)
+ end
+
+ private
+
+ def svn_retry(command)
+ Bundler::Retry.new("svn #{command}", SVNNotAllowedError).attempts do
+ svn(command)
+ end
+ end
+
+ def svn(command, check_errors=true)
+ raise SVNNotAllowedError.new(command) unless allow?
+ out = %x{svn #{command}}
+ raise SVNCommandError.new(command, path) if check_errors && !$?.success?
+ out
+ end
+
+ # Escape the URI for svn 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?
+ @svn ? @svn.allow_svn_ops? : true
+ end
+
+ def in_path(&blk)
+ checkout unless path.exist?
+ SharedHelpers.chdir(path, &blk)
+ end
+ end
+
+ end
+ end
+end
diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb
index 4f537c4a..235f75a2 100644
--- a/lib/bundler/source_list.rb
+++ b/lib/bundler/source_list.rb
@@ -2,11 +2,13 @@ module Bundler
class SourceList
attr_reader :path_sources,
:git_sources,
+ :svn_sources,
:rubygems_sources
def initialize
@path_sources = []
@git_sources = []
+ @svn_sources = []
@rubygems_aggregate = Source::Rubygems.new
@rubygems_sources = [@rubygems_aggregate]
end
@@ -19,6 +21,10 @@ module Bundler
add_source_to_list Source::Git.new(options), git_sources
end
+ def add_svn_source(options = {})
+ add_source_to_list Source::SVN.new(options), svn_sources
+ end
+
def add_rubygems_source(options = {})
add_source_to_list Source::Rubygems.new(options), @rubygems_sources
end
@@ -29,7 +35,7 @@ module Bundler
end
def all_sources
- path_sources + git_sources + rubygems_sources
+ path_sources + git_sources + svn_sources + rubygems_sources
end
def get(source)
@@ -37,11 +43,11 @@ module Bundler
end
def lock_sources
- (path_sources + git_sources) << combine_rubygems_sources
+ (path_sources + git_sources + svn_sources) << combine_rubygems_sources
end
def replace_sources!(replacement_sources)
- [path_sources, git_sources, rubygems_sources].each do |source_list|
+ [path_sources, git_sources, svn_sources, rubygems_sources].each do |source_list|
source_list.map! do |source|
replacement_sources.find { |s| s == source } || source
end
@@ -66,6 +72,7 @@ module Bundler
def source_list_for(source)
case source
when Source::Git then git_sources
+ when Source::SVN then svn_sources
when Source::Path then path_sources
when Source::Rubygems then rubygems_sources
else raise ArgumentError, "Invalid source: #{source.inspect}"
diff --git a/lib/bundler/templates/newgem/LICENSE.txt.tt b/lib/bundler/templates/newgem/LICENSE.txt.tt
index a9f52e6b..de8e4d71 100644
--- a/lib/bundler/templates/newgem/LICENSE.txt.tt
+++ b/lib/bundler/templates/newgem/LICENSE.txt.tt
@@ -1,4 +1,4 @@
-Copyright (c) <%=Time.now.year%> <%=config[:author]%>
+Copyright <%=Time.now.year%> <%=config[:author]%>
MIT License
diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt
index f4fc3c6e..d4848741 100644
--- a/lib/bundler/templates/newgem/Rakefile.tt
+++ b/lib/bundler/templates/newgem/Rakefile.tt
@@ -18,6 +18,8 @@ task :default => :spec
<% if config[:ext] -%>
require "rake/extensiontask"
+task :build => :compile
+
Rake::ExtensionTask.new("<%=config[:underscored_name]%>") do |ext|
ext.lib_dir = "lib/<%=config[:namespaced_path]%>"
end
diff --git a/lib/bundler/templates/newgem/consolerc.tt b/lib/bundler/templates/newgem/consolerc.tt
new file mode 100644
index 00000000..7ed81db6
--- /dev/null
+++ b/lib/bundler/templates/newgem/consolerc.tt
@@ -0,0 +1,3 @@
+# This file is automatically loaded when `bundle console` is run. You can add
+# fixtures and/or initialization code here to make experimenting with your gem
+# easier.
diff --git a/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt b/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt
index 3fd81816..8cfc828f 100644
--- a/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt
+++ b/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt
@@ -1,3 +1,3 @@
require "mkmf"
-create_makefile(<%=config[:underscored_name].inspect%>)
+create_makefile(<%= config[:makefile_path].inspect %>)
diff --git a/lib/bundler/templates/newgem/gitignore.tt b/lib/bundler/templates/newgem/gitignore.tt
index ae3fdc29..ebff7ac5 100644
--- a/lib/bundler/templates/newgem/gitignore.tt
+++ b/lib/bundler/templates/newgem/gitignore.tt
@@ -7,8 +7,10 @@
/pkg/
/spec/reports/
/tmp/
+<%- if config[:ext] -%>
*.bundle
*.so
*.o
*.a
mkmf.log
+<%- end -%>
diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt
index 74d253ec..342f4fe4 100644
--- a/lib/bundler/templates/newgem/newgem.gemspec.tt
+++ b/lib/bundler/templates/newgem/newgem.gemspec.tt
@@ -8,12 +8,18 @@ Gem::Specification.new do |spec|
spec.version = <%=config[:constant_name]%>::VERSION
spec.authors = [<%=config[:author].inspect%>]
spec.email = [<%=config[:email].inspect%>]
-<% if config[:ext] -%>
- spec.extensions = ["ext/<%=config[:underscored_name]%>/extconf.rb"]
+
+<%- if ::Gem::Requirement.new(">= 2.0").satisfied_by? ::Gem::Version.new(::Gem::VERSION) -%>
+ spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' to prevent pushes to rubygems.org, or delete to allow pushes to any server."
+ spec.required_rubygems_version = ">= 2.0"
<% end -%>
- spec.summary = %q{TODO: Write a short summary. Required.}
- spec.description = %q{TODO: Write a longer description. Optional.}
- spec.homepage = ""
+
+<%- if config[:ext] -%>
+ spec.extensions = ["ext/<%=config[:underscored_name]%>/extconf.rb"]
+<%- end -%>
+ spec.summary = %q{TODO: Write a short summary, because Rubygems requires one.}
+ spec.description = %q{TODO: Write a longer description or delete this line.}
+ spec.homepage = "TODO: Put your gem's website or public repo URL here."
spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0")
@@ -23,10 +29,10 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "bundler", "~> <%= Bundler::VERSION.split(".")[0..1].join(".") %>"
spec.add_development_dependency "rake", "~> 10.0"
-<% if config[:ext] -%>
+<%- if config[:ext] -%>
spec.add_development_dependency "rake-compiler"
-<% end -%>
-<% if config[:test] -%>
+<%- end -%>
+<%- if config[:test] -%>
spec.add_development_dependency "<%=config[:test]%>"
-<% end -%>
+<%- end -%>
end
diff --git a/lib/bundler/worker.rb b/lib/bundler/worker.rb
new file mode 100644
index 00000000..49615883
--- /dev/null
+++ b/lib/bundler/worker.rb
@@ -0,0 +1,73 @@
+require 'thread'
+
+module Bundler
+ 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
+ @func = func
+ @threads = size.times.map { |i| Thread.start { process_queue(i) } }
+ trap("INT") { abort_threads }
+ 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
+ raise result.exception if result.is_a?(WrappedException)
+ result
+ end
+
+ def stop
+ stop_threads
+ end
+
+ private
+
+ def process_queue(i)
+ loop do
+ obj = @request_queue.deq
+ break if obj.equal? POISON
+ @response_queue.enq apply_func(obj, i)
+ end
+ end
+
+ def apply_func(obj, i)
+ @func.call(obj, i)
+ rescue Exception => e
+ WrappedException.new(e)
+ end
+
+ # 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 { @request_queue.enq POISON }
+ @threads.each { |thread| thread.join }
+ end
+
+ def abort_threads
+ @threads.each {|i| i.exit }
+ exit 1
+ end
+
+ end
+end
diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn
index d6dfaf50..240cd2d9 100644
--- a/man/bundle-config.ronn
+++ b/man/bundle-config.ronn
@@ -77,7 +77,7 @@ learn more about their operation in [bundle install(1)][bundle-install].
* `path` (`BUNDLE_PATH`):
The location on disk to install gems. Defaults to `$GEM_HOME` in development
- and `vendor/bundler` when `--deployment` is used
+ and `vendor/bundle` when `--deployment` is used
* `frozen` (`BUNDLE_FROZEN`):
Disallow changes to the `Gemfile`. Defaults to `true` when `--deployment`
is used.
@@ -145,11 +145,26 @@ Finally, Bundler also ensures that the current revision in the
`Gemfile.lock` exists in the local git repository. By doing this, Bundler
forces you to fetch the latest changes in the remotes.
-## MIRRORS OF GEM REPOSITORIES
+## MIRRORS OF GEM SOURCES
Bundler supports overriding gem sources with mirrors. This allows you to
configure rubygems.org as the gem source in your Gemfile while still using your
mirror to fetch gems.
+ bundle config mirror.SOURCE_URL MIRROR_URL
+
+For example, to use a mirror of rubygems.org hosted at
+
bundle config mirror.http://rubygems.org http://rubygems-mirror.org
+## CREDENTIALS FOR GEM SOURCES
+
+Bundler allows you to configure credentials for any gem source, which allows
+you to avoid putting secrets into your Gemfile.
+
+ bundle config SOURCE_URL USERNAME:PASSWORD
+
+For example, to save the credentials of user `claudette` for the gem source at
+`gems.longerous.com`, you would run:
+
+ bundle config https://gems.longerous.com/ claudette:s00pers3krit
diff --git a/man/gemfile.5.ronn b/man/gemfile.5.ronn
index f94273af..a068b800 100644
--- a/man/gemfile.5.ronn
+++ b/man/gemfile.5.ronn
@@ -33,6 +33,23 @@ be selected for gems that need to use a non-standard repository, suppressing
this warning, by using the [`:source` option](#SOURCE-source-) or a
[`source` block](#BLOCK-FORM-OF-SOURCE-GIT-PATH-GROUP-and-PLATFORMS).
+### CREDENTIALS (#credentials)
+
+Some gem sources require a username and password. Use `bundle config` to set
+the username and password for any sources that need it. The command must be run
+once on each computer that will install the Gemfile, but this keeps the
+credentials from being stored in plain text in version control.
+
+ bundle config https://gems.example.com/ user:password
+
+For some sources, like a company Gemfury account, it may be easier to simply
+include the credentials in the Gemfile as part of the source URL.
+
+ source "https://user:passowrd@gems.example.com"
+
+Credentials in the source URL will take precedence over credentials set using
+`config`.
+
## RUBY (#ruby)
If your application requires a specific Ruby version or engine, specify your
@@ -229,16 +246,26 @@ gem warning described above in
### GIT (:git)
If necessary, you can specify that a gem is located at a particular
-git repository. The repository can be public (`http://github.com/rails/rails.git`)
-or private (`git@github.com:rails/rails.git`). If the repository is private,
-the user that you use to run `bundle install` `MUST` have the appropriate
-keys available in their `$HOME/.ssh`.
+git repository using the `:git` parameter. The repository can be accessed via
+several protocols:
+
+ * `HTTP(S)`:
+ gem "rails", :git => "https://github.com/rails/rails.git"
+ * `SSH`:
+ gem "rails", :git => "git@github.com:rails/rails.git"
+ * `git`:
+ gem "rails", :git => "git://github.com/rails/rails.git"
-Git repositories are specified using the `:git` parameter. The `group`,
-`platforms`, and `require` options are available and behave exactly the same
-as they would for a normal gem.
+If using SSH, the user that you use to run `bundle install` `MUST` have the
+appropriate keys available in their `$HOME/.ssh`.
- gem "rails", :git => "git://github.com/rails/rails.git"
+`NOTE`: `http://` and `git://` URLs should be avoided if at all possible. These
+protocols are unauthenticated, so a man-in-the-middle attacker can deliver
+malicious code and compromise your system. HTTPS and SSH are strongly
+preferred.
+
+The `group`, `platforms`, and `require` options are available and behave
+exactly the same as they would for a normal gem.
A git repository `SHOULD` have at least one file, at the root of the
directory containing the gem, with the extension `.gemspec`. This file
@@ -255,7 +282,7 @@ to, a version specifier, if provided, means that the git repository is
only valid if the `.gemspec` specifies a version matching the version
specifier. If not, bundler will print a warning.
- gem "rails", "2.3.8", :git => "git://github.com/rails/rails.git"
+ gem "rails", "2.3.8", :git => "https://github.com/rails/rails.git"
# bundle install will fail, because the .gemspec in the rails
# repository's master branch specifies version 3.0.0
@@ -290,8 +317,25 @@ and then installs the resulting gem. The `gem build` command,
which comes standard with Rubygems, evaluates the `.gemspec` in
the context of the directory in which it is located.
+### GIT SOURCE (:git_source)
+
+A custom git source can be defined via the `git_source` method. Provide the source's name
+as an argument, and a block which receives a single argument and interpolates it into a
+string to return the full repo address:
+
+ git_source(:stash){ |repo_name| "https://stash.corp.acme.pl/#{repo_name}.git" }
+ gem 'rails', :stash => 'forks/rails'
+
+In addition, if you wish to choose a specific branch:
+
+ gem "rails", :stash => "forks/rails", :branch => "branch_name"
+
### GITHUB (:github)
+`NOTE`: This shorthand should be avoided until Bundler 2.0, since it
+currently expands to an insecure `git://` URL. This allows a
+man-in-the-middle attacker to compromise your system.
+
If the git repository you want to use is hosted on GitHub and is public, you can use the
:github shorthand to specify just the github username and repository name (without the
trailing ".git"), separated by a slash. If both the username and repository name are the
@@ -304,9 +348,36 @@ Are both equivalent to
gem "rails", :git => "git://github.com/rails/rails.git"
-In addition, if you wish to choose a specific branch:
+Since the `github` method is a specialization of `git_source`, it accepts a `:branch` named argument.
+
+### GIST (:gist)
+
+If the git repository you want to use is hosted as a Github Gist and is public, you can use
+the :gist shorthand to specify just the gist identifier (without the trailing ".git").
+
+ gem "the_hatch", :gist => "4815162342"
+
+Is equivalent to:
+
+ gem "the_hatch", :git => "https://gist.github.com/4815162342.git"
+
+Since the `gist` method is a specialization of `git_source`, it accepts a `:branch` named argument.
+
+### BITBUCKET (:bitbucket)
+
+If the git repository you want to use is hosted on Bitbucket and is public, you can use the
+:bitbucket shorthand to specify just the bitbucket username and repository name (without the
+trailing ".git"), separated by a slash. If both the username and repository name are the
+same, you can omit one.
+
+ gem "rails", :bitbucket => "rails/rails"
+ gem "rails", :bitbucket => "rails"
+
+Are both equivalent to
+
+ gem "rails", :git => "https://rails@bitbucket.org/rails/rails.git"
- gem "rails", :github => "rails/rails", :branch => "branch_name"
+Since the `bitbucket` method is a specialization of `git_source`, it accepts a `:branch` named argument.
### PATH (:path)
@@ -334,7 +405,7 @@ applied to a group of gems by using block form.
gem "another_internal_gem"
end
- git "git://github.com/rails/rails.git" do
+ git "https://github.com/rails/rails.git" do
gem "activesupport"
gem "actionpack"
end
diff --git a/spec/bundler/dsl_spec.rb b/spec/bundler/dsl_spec.rb
index 03db5d20..831a01f0 100644
--- a/spec/bundler/dsl_spec.rb
+++ b/spec/bundler/dsl_spec.rb
@@ -6,7 +6,7 @@ describe Bundler::Dsl do
allow(Bundler::Source::Rubygems).to receive(:new){ @rubygems }
end
- describe "#register_host" do
+ describe "#git_source" do
it "registers custom hosts" do
subject.git_source(:example){ |repo_name| "git@git.example.com:#{repo_name}.git" }
subject.git_source(:foobar){ |repo_name| "git@foobar.com:#{repo_name}.git" }
@@ -49,6 +49,18 @@ describe Bundler::Dsl do
github_uri = "git://github.com/rails/rails.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
+
+ it "converts :bitbucket to :git" do
+ subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails")
+ bitbucket_uri = "https://mcorp@bitbucket.org/mcorp/flatlab-rails.git"
+ expect(subject.dependencies.first.source.uri).to eq(bitbucket_uri)
+ end
+
+ it "converts 'mcorp' to 'mcorp/mcorp'" do
+ subject.gem("not-really-a-gem", :bitbucket => "mcorp")
+ bitbucket_uri = "https://mcorp@bitbucket.org/mcorp/mcorp.git"
+ expect(subject.dependencies.first.source.uri).to eq(bitbucket_uri)
+ end
end
end
@@ -72,6 +84,61 @@ describe Bundler::Dsl do
end
end
+ describe "#gem" do
+ [:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :mri, :mri_18, :mri_19,
+ :mri_20, :mri_21, :jruby, :rbx].each do |platform|
+ it "allows #{platform} as a valid platform" do
+ subject.gem("foo", :platform => platform)
+ end
+ end
+
+ it "rejects invalid platforms" do
+ expect { subject.gem("foo", :platform => :bogus) }.
+ to raise_error(Bundler::GemfileError, /is not a valid platform/)
+ end
+
+ it "rejects with a leading space in the name" do
+ expect { subject.gem(" foo") }.
+ to raise_error(Bundler::GemfileError, /' foo' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a trailing space in the name" do
+ expect { subject.gem("foo ") }.
+ to raise_error(Bundler::GemfileError, /'foo ' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a space in the gem name" do
+ expect { subject.gem("fo o") }.
+ to raise_error(Bundler::GemfileError, /'fo o' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a tab in the gem name" do
+ expect { subject.gem("fo\to") }.
+ to raise_error(Bundler::GemfileError, /'fo\to' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a newline in the gem name" do
+ expect { subject.gem("fo\no") }.
+ to raise_error(Bundler::GemfileError, /'fo\no' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a carriage return in the gem name" do
+ expect { subject.gem("fo\ro") }.
+ to raise_error(Bundler::GemfileError, /'fo\ro' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects with a form feed in the gem name" do
+ expect { subject.gem("fo\fo") }.
+ to raise_error(Bundler::GemfileError, /'fo\fo' is not a valid gem name because it contains whitespace/)
+ end
+
+ it "rejects symbols as gem name" do
+ expect { subject.gem(:foo) }.
+ to raise_error(Bundler::GemfileError, /You need to specify gem names as Strings. Use 'gem "foo"' instead/)
+ end
+
+ end
+
describe "syntax errors" do
it "will raise a Bundler::GemfileError" do
gemfile "gem 'foo', :path => /unquoted/string/syntax/error"
diff --git a/spec/bundler/gem_helper_spec.rb b/spec/bundler/gem_helper_spec.rb
index 791a78b3..e983c240 100644
--- a/spec/bundler/gem_helper_spec.rb
+++ b/spec/bundler/gem_helper_spec.rb
@@ -65,6 +65,7 @@ describe Bundler::GemHelper do
before(:each) do
content = app_gemspec_content.gsub("TODO: ", "")
+ content.sub!(/homepage\s+= ".*"/, 'homepage = ""')
File.open(app_gemspec_path, "w") { |file| file << content }
end
@@ -84,7 +85,8 @@ describe Bundler::GemHelper do
end
context "defines Rake tasks" do
- let(:task_names) { %w[build install release] }
+ let(:task_names) { %w[build install release
+ release:guard_clean release:source_control_push release:rubygem_push] }
context "before installation" do
it "raises an error with appropriate message" do
@@ -156,24 +158,36 @@ describe Bundler::GemHelper do
end
end
- describe "#release_gem" do
+ describe "rake release" do
+ let!(:rake_application) { Rake.application }
+
+ before(:each) do
+ Rake.application = Rake::Application.new
+ subject.install
+ end
+
+ after(:each) do
+ Rake.application = rake_application
+ end
+
before do
Dir.chdir(app_path) do
`git init`
`git config user.email "you@example.com"`
`git config user.name "name"`
+ `git config push.default simple`
end
end
context "fails" do
it "when there are unstaged files" do
- expect { subject.release_gem }.
+ expect { Rake.application["release"].invoke }.
to raise_error("There are files that need to be committed first.")
end
it "when there are uncommitted files" do
Dir.chdir(app_path) { `git add .` }
- expect { subject.release_gem }.
+ expect { Rake.application["release"].invoke }.
to raise_error("There are files that need to be committed first.")
end
@@ -183,7 +197,7 @@ describe Bundler::GemHelper do
allow(Bundler.ui).to receive(:error)
Dir.chdir(app_path) { `git commit -a -m "initial commit"` }
- expect { subject.release_gem }.to raise_error
+ expect { Rake.application["release"].invoke }.to raise_error
end
end
@@ -202,9 +216,9 @@ describe Bundler::GemHelper do
mock_confirm_message "Pushed git commits and tags."
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
- Dir.chdir(app_path) { sys_exec("git push origin master", true) }
+ Dir.chdir(app_path) { sys_exec("git push -u origin master", true) }
- subject.release_gem
+ Rake.application["release"].invoke
end
it "even if tag already exists" do
@@ -216,7 +230,7 @@ describe Bundler::GemHelper do
`git tag -a -m \"Version #{app_version}\" v#{app_version}`
end
- subject.release_gem
+ Rake.application["release"].invoke
end
end
end
diff --git a/spec/bundler/s3_fetcher_spec.rb b/spec/bundler/s3_fetcher_spec.rb
new file mode 100644
index 00000000..3402e06d
--- /dev/null
+++ b/spec/bundler/s3_fetcher_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe Bundler::S3Fetcher do
+ before do
+ allow(Bundler).to receive(:root){ Pathname.new("root") }
+ end
+
+ describe "sign" do
+ it "requires authentication" do
+ url = "s3://foo"
+ expect { Bundler::S3Fetcher.new(url).sign(URI(url))}.to raise_error(Bundler::Fetcher::AuthenticationRequiredError)
+ end
+
+ it "signs S3 requests" do
+ accessId = "a"
+ secretKey = "b"
+ url = "s3://#{accessId}:#{secretKey}@foo"
+ time = Time.utc(2014,6,1).to_i
+
+ actual = Bundler::S3Fetcher.new(url).sign(URI(url),time)
+ expect(actual.host).to eq "foo.s3.amazonaws.com"
+ expect(actual.scheme).to eq "https"
+ query = CGI.parse(actual.query)
+ expect(query['AWSAccessKeyId']).to eq [accessId]
+ expect(query['Expires']).to eq [time.to_s]
+ expect(query['Signature']).to eq ["2ZFX8vg7E04u/UqUH9F/cKiQjJA="]
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/bundler/source/rubygems_spec.rb b/spec/bundler/source/rubygems_spec.rb
index 68fc6c6e..5be40715 100644
--- a/spec/bundler/source/rubygems_spec.rb
+++ b/spec/bundler/source/rubygems_spec.rb
@@ -22,4 +22,15 @@ describe Bundler::Source::Rubygems do
end
end
end
+
+ describe "#fetchers" do
+ let(:remotes) { [URI("s3://foo"), URI("http://foo")] }
+ subject(:source) { Bundler::Source::Rubygems.new("remotes" => remotes) }
+
+ it "turns s3 paths into S3Fetcher objects and other paths into Fetcher objects" do
+ result = source.fetchers
+ expect(result.first).to be_an_instance_of Bundler::S3Fetcher
+ expect(result.last).to be_an_instance_of Bundler::Fetcher
+ end
+ end
end
diff --git a/spec/bundler/source_list_spec.rb b/spec/bundler/source_list_spec.rb
index e74337f6..1110f661 100644
--- a/spec/bundler/source_list_spec.rb
+++ b/spec/bundler/source_list_spec.rb
@@ -62,6 +62,29 @@ describe Bundler::SourceList do
end
end
+ describe "#add_svn_source" do
+ before do
+ @duplicate = source_list.add_svn_source('uri' => 'svn://host/path')
+ @new_source = source_list.add_svn_source('uri' => 'svn://host/path')
+ end
+
+ it "returns the new svn source" do
+ expect(@new_source).to be_instance_of(Bundler::Source::SVN)
+ end
+
+ it "passes the provided options to the new source" do
+ expect(@new_source.options).to eq('uri' => 'svn://host/path')
+ end
+
+ it "adds the source to the beginning of svn_sources" do
+ expect(source_list.svn_sources.first).to equal(@new_source)
+ end
+
+ it "removes existing duplicates" do
+ expect(source_list.svn_sources).not_to include equal(@duplicate)
+ end
+ end
+
describe "#add_rubygems_source" do
before do
@duplicate = source_list.add_rubygems_source('remotes' => ['https://rubygems.org/'])
@@ -117,20 +140,23 @@ describe Bundler::SourceList do
expect(source_list.all_sources).to include rubygems_aggregate
end
- it "returns path sources before git sources before rubygems sources before the aggregate" do
+ it "returns path sources before git sources before svn sources before rubygems sources before the aggregate" do
source_list.add_git_source('uri' => 'git://host/path.git')
source_list.add_rubygems_source('remotes' => ['https://rubygems.org'])
source_list.add_path_source('path' => '/path/to/gem')
+ source_list.add_svn_source('uri' => 'svn://host/path')
expect(source_list.all_sources).to eq [
Bundler::Source::Path.new('path' => '/path/to/gem'),
Bundler::Source::Git.new('uri' => 'git://host/path.git'),
+ Bundler::Source::SVN.new('uri' => 'svn://host/path'),
Bundler::Source::Rubygems.new('remotes' => ['https://rubygems.org']),
rubygems_aggregate,
]
end
it "returns sources of the same type in the reverse order that they were added" do
+ source_list.add_svn_source('uri' => 'svn://second-svn.org/path')
source_list.add_git_source('uri' => 'git://third-git.org/path.git')
source_list.add_rubygems_source('remotes' => ['https://fifth-rubygems.org'])
source_list.add_path_source('path' => '/third/path/to/gem')
@@ -142,6 +168,7 @@ describe Bundler::SourceList do
source_list.add_path_source('path' => '/first/path/to/gem')
source_list.add_rubygems_source('remotes' => ['https://first-rubygems.org'])
source_list.add_git_source('uri' => 'git://first-git.org/path.git')
+ source_list.add_svn_source('uri' => 'svn://first-svn.org/path')
expect(source_list.all_sources).to eq [
Bundler::Source::Path.new('path' => '/first/path/to/gem'),
@@ -150,6 +177,8 @@ describe Bundler::SourceList do
Bundler::Source::Git.new('uri' => 'git://first-git.org/path.git'),
Bundler::Source::Git.new('uri' => 'git://second-git.org/path.git'),
Bundler::Source::Git.new('uri' => 'git://third-git.org/path.git'),
+ Bundler::Source::SVN.new('uri' => 'svn://first-svn.org/path'),
+ Bundler::Source::SVN.new('uri' => 'svn://second-svn.org/path'),
Bundler::Source::Rubygems.new('remotes' => ['https://first-rubygems.org']),
Bundler::Source::Rubygems.new('remotes' => ['https://second-rubygems.org']),
Bundler::Source::Rubygems.new('remotes' => ['https://third-rubygems.org']),
@@ -217,6 +246,35 @@ describe Bundler::SourceList do
end
end
+ describe "#svn_sources" do
+ it "returns an empty array when no svn sources have been added" do
+ source_list.add_rubygems_remote('https://rubygems.org')
+ source_list.add_path_source('path' => '/path/to/gem')
+
+ expect(source_list.svn_sources).to be_empty
+ end
+
+ it "returns svn sources in the reverse order that they were added" do
+ source_list.add_svn_source('uri' => 'svn://third-svn.org/path')
+ source_list.add_rubygems_remote('https://fifth-rubygems.org')
+ source_list.add_path_source('path' => '/third/path/to/gem')
+ source_list.add_rubygems_remote('https://fourth-rubygems.org')
+ source_list.add_path_source('path' => '/second/path/to/gem')
+ source_list.add_rubygems_remote('https://third-rubygems.org')
+ source_list.add_svn_source('uri' => 'svn://second-svn.org/path')
+ source_list.add_rubygems_remote('https://second-rubygems.org')
+ source_list.add_path_source('path' => '/first/path/to/gem')
+ source_list.add_rubygems_remote('https://first-rubygems.org')
+ source_list.add_svn_source('uri' => 'svn://first-svn.org/path')
+
+ expect(source_list.svn_sources).to eq [
+ Bundler::Source::SVN.new('uri' => 'svn://first-svn.org/path'),
+ Bundler::Source::SVN.new('uri' => 'svn://second-svn.org/path'),
+ Bundler::Source::SVN.new('uri' => 'svn://third-svn.org/path'),
+ ]
+ end
+ end
+
describe "#rubygems_sources" do
it "includes the aggregate rubygems source when rubygems sources have been added" do
source_list.add_git_source('uri' => 'git://host/path.git')
@@ -278,6 +336,7 @@ describe Bundler::SourceList do
describe "#lock_sources" do
it "combines the rubygems sources into a single instance, removing duplicate remotes from the front" do
+ source_list.add_svn_source('uri' => 'svn://second-svn.org/path')
source_list.add_git_source('uri' => 'git://third-git.org/path.git')
source_list.add_rubygems_source('remotes' => ['https://fourth-rubygems.org']) # intentional duplicate
source_list.add_path_source('path' => '/third/path/to/gem')
@@ -289,6 +348,7 @@ describe Bundler::SourceList do
source_list.add_path_source('path' => '/first/path/to/gem')
source_list.add_rubygems_source('remotes' => ['https://fourth-rubygems.org'])
source_list.add_git_source('uri' => 'git://first-git.org/path.git')
+ source_list.add_svn_source('uri' => 'svn://first-svn.org/path')
expect(source_list.lock_sources).to eq [
Bundler::Source::Path.new('path' => '/first/path/to/gem'),
@@ -297,6 +357,8 @@ describe Bundler::SourceList do
Bundler::Source::Git.new('uri' => 'git://first-git.org/path.git'),
Bundler::Source::Git.new('uri' => 'git://second-git.org/path.git'),
Bundler::Source::Git.new('uri' => 'git://third-git.org/path.git'),
+ Bundler::Source::SVN.new('uri' => 'svn://first-svn.org/path'),
+ Bundler::Source::SVN.new('uri' => 'svn://second-svn.org/path'),
Bundler::Source::Rubygems.new('remotes' => [
'https://first-rubygems.org',
'https://second-rubygems.org',
@@ -335,11 +397,13 @@ describe Bundler::SourceList do
describe "#cached!" do
let(:rubygems_source) { source_list.add_rubygems_remote('https://rubygems.org') }
let(:git_source) { source_list.add_git_source('uri' => 'git://host/path.git') }
+ let(:svn_source) { source_list.add_svn_source('uri' => 'svn://host/path') }
let(:path_source) { source_list.add_path_source('path' => '/path/to/gem') }
it "calls #cached! on all the sources" do
expect(rubygems_source).to receive(:cached!)
expect(git_source).to receive(:cached!)
+ expect(svn_source).to receive(:cached!)
expect(path_source).to receive(:cached!)
source_list.cached!
end
@@ -348,11 +412,13 @@ describe Bundler::SourceList do
describe "#remote!" do
let(:rubygems_source) { source_list.add_rubygems_remote('https://rubygems.org') }
let(:git_source) { source_list.add_git_source('uri' => 'git://host/path.git') }
+ let(:svn_source) { source_list.add_git_source('uri' => 'svn://host/path') }
let(:path_source) { source_list.add_path_source('path' => '/path/to/gem') }
it "calls #remote! on all the sources" do
expect(rubygems_source).to receive(:remote!)
expect(git_source).to receive(:remote!)
+ expect(svn_source).to receive(:remote!)
expect(path_source).to receive(:remote!)
source_list.remote!
end
diff --git a/spec/cache/gems_spec.rb b/spec/cache/gems_spec.rb
index b70f6274..9386164c 100644
--- a/spec/cache/gems_spec.rb
+++ b/spec/cache/gems_spec.rb
@@ -151,6 +151,38 @@ describe "bundle cache" do
end
end
+ describe "when there are also svn sources" do
+ before do
+ build_svn "foo"
+ system_gems "rack-1.0.0"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ svn "file://#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ gem 'rack'
+ G
+ end
+
+ it "still works" do
+ bundle :cache
+
+ system_gems []
+ bundle "install --local"
+
+ should_be_installed("rack 1.0.0", "foo 1.0")
+ end
+
+ it "should not explode if the lockfile is not present" do
+ FileUtils.rm(bundled_app("Gemfile.lock"))
+
+ bundle :cache
+
+ expect(bundled_app("Gemfile.lock")).to exist
+ end
+ end
+
describe "when previously cached" do
before :each do
build_repo2
diff --git a/spec/cache/platform_spec.rb b/spec/cache/platform_spec.rb
index 0ced6637..d9730f54 100644
--- a/spec/cache/platform_spec.rb
+++ b/spec/cache/platform_spec.rb
@@ -5,17 +5,13 @@ describe "bundle cache with multiple platforms" do
gemfile <<-G
source "file://#{gem_repo1}"
- platforms :ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21 do
+ platforms :mri, :rbx do
gem "rack", "1.0.0"
end
platforms :jruby do
gem "activesupport", "2.3.5"
end
-
- platforms :mri, :mri_18, :mri_19, :mri_20, :mri_21 do
- gem "activerecord", "2.3.2"
- end
G
lockfile <<-G
@@ -24,7 +20,6 @@ describe "bundle cache with multiple platforms" do
specs:
rack (1.0.0)
activesupport (2.3.5)
- activerecord (2.3.2)
PLATFORMS
ruby
@@ -33,25 +28,26 @@ describe "bundle cache with multiple platforms" do
DEPENDENCIES
rack (1.0.0)
activesupport (2.3.5)
- activerecord (2.3.2)
G
- cache_gems "rack-1.0.0", "activesupport-2.3.5", "activerecord-2.3.2"
+ cache_gems "rack-1.0.0", "activesupport-2.3.5"
end
- it "ensures that bundle install does not delete gems for other platforms" do
- bundle "install"
+ it "ensures that a succesful bundle install does not delete gems for other platforms" do
+ bundle "install", :exitstatus => true
+
+ expect(exitstatus).to eq 0
expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/activesupport-2.3.5.gem")).to exist
- expect(bundled_app("vendor/cache/activerecord-2.3.2.gem")).to exist
end
- it "ensures that bundle update does not delete gems for other platforms" do
- bundle "update"
+ it "ensures that a succesful bundle update does not delete gems for other platforms" do
+ bundle "update", :exitstatus => true
+
+ expect(exitstatus).to eq 0
expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/activesupport-2.3.5.gem")).to exist
- expect(bundled_app("vendor/cache/activerecord-2.3.2.gem")).to exist
end
end
diff --git a/spec/cache/svn_spec.rb b/spec/cache/svn_spec.rb
new file mode 100644
index 00000000..e798e0e9
--- /dev/null
+++ b/spec/cache/svn_spec.rb
@@ -0,0 +1,82 @@
+require "spec_helper"
+
+%w(cache package).each do |cmd|
+ describe "bundle #{cmd} with svn" do
+ it "copies repository to vendor cache and uses it" do
+ svn = build_svn "foo"
+ ref = svn.ref_for("HEAD")
+
+ install_gemfile <<-G
+ gem "foo", :svn => 'file://#{lib_path("foo-1.0")}'
+ G
+
+ bundle "#{cmd} --all"
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.svn")).to exist
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ should_be_installed "foo 1.0"
+ end
+
+ it "copies repository to vendor cache and uses it even when installed with bundle --path" do
+ svn = build_svn "foo"
+ ref = svn.ref_for("HEAD")
+
+ install_gemfile <<-G
+ gem "foo", :svn => 'file://#{lib_path("foo-1.0")}'
+ G
+
+ bundle "install --path vendor/bundle"
+ bundle "#{cmd} --all"
+
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.svn")).to exist
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ should_be_installed "foo 1.0"
+ end
+
+ it "runs twice without exploding" do
+ build_svn "foo"
+
+ install_gemfile <<-G
+ gem "foo", :svn => 'file://#{lib_path("foo-1.0")}'
+ G
+
+ bundle "#{cmd} --all"
+ bundle "#{cmd} --all"
+
+ expect(err).to eq("")
+ FileUtils.rm_rf lib_path("foo-1.0")
+ should_be_installed "foo 1.0"
+ end
+
+ it "tracks updates" do
+ svn = build_svn "foo"
+ old_ref = svn.ref_for("HEAD")
+
+ install_gemfile <<-G
+ gem "foo", :svn => 'file://#{lib_path("foo-1.0")}'
+ G
+
+ bundle "#{cmd} --all"
+
+ update_svn "foo" do |s|
+ s.write "lib/foo.rb", "puts :CACHE"
+ end
+
+ ref = svn.ref_for("HEAD")
+ expect(ref).not_to eq(old_ref)
+
+ bundle "update"
+ bundle "#{cmd} --all"
+
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+
+ FileUtils.rm_rf lib_path("foo-1.0")
+ run "require 'foo'"
+ expect(out).to eq("CACHE")
+ end
+
+ end
+end
diff --git a/spec/commands/binstubs_spec.rb b/spec/commands/binstubs_spec.rb
index e307539f..cb4dd527 100644
--- a/spec/commands/binstubs_spec.rb
+++ b/spec/commands/binstubs_spec.rb
@@ -61,6 +61,21 @@ describe "bundle binstubs <gem>" do
expect(out).to eq("Sorry, Bundler can only be run via Rubygems.")
end
+ it "installs binstubs from svn gems" do
+ FileUtils.mkdir_p(lib_path("foo/bin"))
+ FileUtils.touch(lib_path("foo/bin/foo"))
+ build_svn "foo", "1.0", :path => lib_path("foo") do |s|
+ s.executables = %w(foo)
+ end
+ install_gemfile <<-G
+ gem "foo", :svn => "file://#{lib_path('foo')}"
+ G
+
+ bundle "binstubs foo"
+
+ expect(bundled_app("bin/foo")).to exist
+ end
+
it "installs binstubs from git gems" do
FileUtils.mkdir_p(lib_path("foo/bin"))
FileUtils.touch(lib_path("foo/bin/foo"))
@@ -216,4 +231,29 @@ describe "bundle binstubs <gem>" do
expect(out).to include('no executables for the gem with_development_dependency')
end
end
+
+ context "when BUNDLE_INSTALL is specified" do
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config auto_install 1"
+ bundle "binstubs rack"
+ expect(out).to include('Installing rack 1.0.0')
+ should_be_installed "rack 1.0.0"
+ end
+
+ it "does nothing when already up to date" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config auto_install 1"
+ bundle "binstubs rack", :env => { "BUNDLE_INSTALL" => 1 }
+ expect(out).not_to include('Installing rack 1.0.0')
+ end
+ end
end
diff --git a/spec/commands/clean_spec.rb b/spec/commands/clean_spec.rb
index cee1312a..9279e884 100644
--- a/spec/commands/clean_spec.rb
+++ b/spec/commands/clean_spec.rb
@@ -145,6 +145,42 @@ describe "bundle clean" do
expect(vendored_gems("cache/bundler/git/foo-1.0-#{digest}")).to exist
end
+ it "removes unused svn gems" do
+ build_svn "foo", :path => lib_path("foo")
+ svn_path = lib_path('foo')
+ revision = 1
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ svn "file://#{svn_path}", :ref => "#{revision}" do
+ gem "foo"
+ end
+ G
+
+ bundle "install --path vendor/bundle"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ G
+ bundle "install"
+
+ bundle :clean
+
+ expect(out).to eq("Removing foo (#{revision})")
+
+ expect(vendored_gems("gems/rack-1.0.0")).to exist
+ expect(vendored_gems("bundler/gems/foo-#{revision}")).not_to exist
+ expect(vendored_gems("cache/bundler/svn/foo-#{revision}")).not_to exist
+
+ expect(vendored_gems("specifications/rack-1.0.0.gemspec")).to exist
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
it "removes unused git gems" do
build_git "foo", :path => lib_path("foo")
git_path = lib_path('foo')
@@ -284,7 +320,7 @@ describe "bundle clean" do
bundle :clean, :exitstatus => true
expect(exitstatus).to eq(1)
- expect(out).to eq("Can only use bundle clean when --path is set or --force is set")
+ expect(out).to include("--force")
end
# handling bundle clean upgrade path from the pre's
@@ -589,4 +625,28 @@ describe "bundle clean" do
expect(vendored_gems("bin/rackup")).to exist
end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "foo"
+ G
+
+ bundle "install --path vendor/bundle --no-clean"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "thin"
+ gem "weakling"
+ G
+
+ bundle "config auto_install 1"
+ bundle :clean
+ expect(out).to include('Installing weakling 0.0.3')
+ should_have_gems 'thin-1.0', 'rack-1.0.0', 'weakling-0.0.3'
+ should_not_have_gems 'foo-1.0'
+ end
end
diff --git a/spec/commands/console_spec.rb b/spec/commands/console_spec.rb
index 75d6796c..8c6e184c 100644
--- a/spec/commands/console_spec.rb
+++ b/spec/commands/console_spec.rb
@@ -36,9 +36,16 @@ describe "bundle console" do
input.puts("__callee__")
input.puts("exit")
end
- expect(out).to include("irb")
+ expect(out).to include("IRB")
end
+ it "loads up .consolerc if it exists" do
+ consolerc <<-C
+ puts "Hello!"
+ C
+ bundle "console"
+ expect(out).to include("Hello!")
+ end
it "doesn't load any other groups" do
bundle "console" do |input|
@@ -73,4 +80,23 @@ describe "bundle console" do
expect(out).to include("NameError")
end
end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :group => :test
+ gem "rack_middleware", :group => :development
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :console do |input|
+ input.puts("puts 'hello'")
+ input.puts("exit")
+ end
+ expect(out).to include("Installing foo 1.0")
+ expect(out).to include("hello")
+ should_be_installed "foo 1.0"
+ end
end
diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb
index 52b93565..ad91d085 100644
--- a/spec/commands/exec_spec.rb
+++ b/spec/commands/exec_spec.rb
@@ -306,4 +306,62 @@ describe "bundle exec" do
end
end
end
+
+ describe "from gems bundled via :svn" do
+ before(:each) do
+ build_svn "fizz_svn" do |s|
+ s.executables = "fizz_svn"
+ end
+
+ install_gemfile <<-G
+ gem "fizz_svn", :svn => "file://#{lib_path('fizz_svn-1.0')}"
+ G
+ end
+
+ it "works when unlocked" do
+ bundle "exec fizz_svn"
+ expect(out).to eq("1.0")
+ end
+
+ it "works when locked" do
+ should_be_locked
+ bundle "exec fizz_svn"
+ expect(out).to eq("1.0")
+ end
+ end
+
+ describe "from gems bundled via :svn with no gemspec" do
+ before(:each) do
+ build_svn "fizz_no_gemspec", :gemspec => false do |s|
+ s.executables = "fizz_no_gemspec"
+ end
+
+ install_gemfile <<-G
+ gem "fizz_no_gemspec", "1.0", :svn => "file://#{lib_path('fizz_no_gemspec-1.0')}"
+ G
+ end
+
+ it "works when unlocked" do
+ bundle "exec fizz_no_gemspec"
+ expect(out).to eq("1.0")
+ end
+
+ it "works when locked" do
+ should_be_locked
+ bundle "exec fizz_no_gemspec"
+ expect(out).to eq("1.0")
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle "exec rackup"
+ expect(out).to include("Installing foo 1.0")
+ end
end
diff --git a/spec/commands/licenses_spec.rb b/spec/commands/licenses_spec.rb
index c8d5ff73..e2aba5a7 100644
--- a/spec/commands/licenses_spec.rb
+++ b/spec/commands/licenses_spec.rb
@@ -15,4 +15,17 @@ describe "bundle licenses" do
expect(out).to include("actionpack: Unknown")
expect(out).to include("with_license: MIT")
end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "with_license"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :licenses
+ expect(out).to include("Installing foo 1.0")
+ end
end
diff --git a/spec/commands/newgem_spec.rb b/spec/commands/newgem_spec.rb
index fc0e782e..bb7d97fc 100644
--- a/spec/commands/newgem_spec.rb
+++ b/spec/commands/newgem_spec.rb
@@ -53,6 +53,8 @@ describe "bundle gem" do
expect(bundled_app("test_gem/Rakefile")).to exist
expect(bundled_app("test_gem/lib/test_gem.rb")).to exist
expect(bundled_app("test_gem/lib/test_gem/version.rb")).to exist
+ expect(bundled_app("test_gem/.gitignore")).to exist
+ expect(bundled_app("test_gem/.consolerc")).to exist
end
it "starts with version 0.0.1" do
@@ -78,6 +80,11 @@ describe "bundle gem" do
it_should_behave_like "git config is absent"
end
+ it "sets gemspec metadata['allowed_push_host']", :rubygems => "2.0" do
+ expect(generated_gem.gemspec.metadata['allowed_push_host']).
+ to match("delete to allow pushes to any server")
+ end
+
it "sets gemspec license to MIT by default" do
expect(generated_gem.gemspec.license).to eq("MIT")
end
@@ -254,6 +261,11 @@ describe "bundle gem" do
it_should_behave_like "git config is absent"
end
+ it "sets gemspec metadata['allowed_push_host']", :rubygems => "2.0" do
+ expect(generated_gem.gemspec.metadata['allowed_push_host']).
+ to match("delete to allow pushes to any server")
+ end
+
it "sets gemspec license to MIT by default" do
expect(generated_gem.gemspec.license).to eq("MIT")
end
@@ -418,6 +430,22 @@ describe "bundle gem" do
it "includes rake-compiler" do
expect(bundled_app("test_gem/test_gem.gemspec").read).to include('spec.add_development_dependency "rake-compiler"')
end
+
+ it "depends on compile task for build" do
+ rakefile = strip_whitespace <<-RAKEFILE
+ require "bundler/gem_tasks"
+
+ require "rake/extensiontask"
+
+ task :build => :compile
+
+ Rake::ExtensionTask.new("test_gem") do |ext|
+ ext.lib_dir = "lib/test_gem"
+ end
+ RAKEFILE
+
+ expect(bundled_app("test_gem/Rakefile").read).to eq(rakefile)
+ end
end
end
end
diff --git a/spec/commands/open_spec.rb b/spec/commands/open_spec.rb
index e3a66ec1..67ee40cf 100644
--- a/spec/commands/open_spec.rb
+++ b/spec/commands/open_spec.rb
@@ -65,4 +65,16 @@ describe "bundle open" do
expect(out).to match(/bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}\z/)
end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle "open rails", :env => {"EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => ""}
+ expect(out).to include("Installing foo 1.0")
+ end
end
diff --git a/spec/commands/outdated_spec.rb b/spec/commands/outdated_spec.rb
index da17a2ee..fc6b4fbe 100644
--- a/spec/commands/outdated_spec.rb
+++ b/spec/commands/outdated_spec.rb
@@ -153,4 +153,16 @@ describe "bundle outdated" do
expect(exitstatus).to_not be_zero
end
end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :outdated
+ expect(out).to include("Installing foo 1.0")
+ end
end
diff --git a/spec/commands/package_spec.rb b/spec/commands/package_spec.rb
index e433b920..e193e239 100644
--- a/spec/commands/package_spec.rb
+++ b/spec/commands/package_spec.rb
@@ -28,6 +28,20 @@ describe "bundle package" do
expect(bundled_app("test/vendor/cache/")).to exist
end
end
+
+ context "with --no-install" do
+ it "puts the gems in vendor/cache but does not install them" do
+ gemfile <<-D
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ D
+
+ bundle "package --no-install"
+
+ should_not_be_installed "rack 1.0.0"
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ end
+ end
end
describe "bundle install with gem sources" do
diff --git a/spec/commands/show_spec.rb b/spec/commands/show_spec.rb
index 01a2c2a5..19b9e84f 100644
--- a/spec/commands/show_spec.rb
+++ b/spec/commands/show_spec.rb
@@ -109,6 +109,22 @@ describe "bundle show" do
end
end
+ context "with a svn repo in the Gemfile" do
+ before :each do
+ @svn = build_svn "foo", "1.0"
+ end
+
+ it "prints out svn info" do
+ install_gemfile <<-G
+ gem "foo", :svn => "file://#{lib_path('foo-1.0')}"
+ G
+ should_be_installed "foo 1.0"
+
+ bundle :show
+ expect(out).to include("foo (1.0 1")
+ end
+ end
+
context "in a fresh gem in a blank git repo" do
before :each do
build_git "foo", :path => lib_path("foo")
@@ -122,4 +138,29 @@ describe "bundle show" do
expect(err).to be_empty
end
end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo"
+ G
+
+ bundle "config auto_install 1"
+ bundle :show
+ expect(out).to include("Installing foo 1.0")
+ end
+
+ context "with an invalid regexp for gem name" do
+ it "does not find the gem" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+
+ invalid_regexp = '[]'
+
+ bundle "show #{invalid_regexp}"
+ expect(out).to include("Could not find gem '#{invalid_regexp}'.")
+ end
+ end
end
diff --git a/spec/install/deploy_spec.rb b/spec/install/deploy_spec.rb
index c3ba3215..9116a347 100644
--- a/spec/install/deploy_spec.rb
+++ b/spec/install/deploy_spec.rb
@@ -46,6 +46,18 @@ describe "install with --deployment or --frozen" do
expect(exitstatus).to eq(0)
end
+ it "works if you exclude a group with a svn gem" do
+ build_svn "foo"
+ gemfile <<-G
+ group :test do
+ gem "foo", :svn => "file://#{lib_path('foo-1.0')}"
+ end
+ G
+ bundle :install
+ bundle "install --deployment --without test", :exitstatus => true
+ expect(exitstatus).to eq(0)
+ end
+
it "works when you bundle exec bundle" do
bundle :install
bundle "install --deployment"
diff --git a/spec/install/gemfile/svn_spec.rb b/spec/install/gemfile/svn_spec.rb
new file mode 100644
index 00000000..5b2353f8
--- /dev/null
+++ b/spec/install/gemfile/svn_spec.rb
@@ -0,0 +1,582 @@
+require "spec_helper"
+
+describe "bundle install with svn sources" do
+ describe "when floating on master" do
+ before :each do
+ build_svn "foo" do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ svn "file://#{lib_path('foo-1.0')}" do
+ gem 'foo'
+ end
+ G
+ end
+
+ it "sets up svn gem executables on the path" do
+ pending_jruby_shebang_fix
+ bundle "exec foobar"
+ expect(out).to eq("1.0")
+ end
+
+ it "complains if pinned specs don't exist in the svn repo" do
+ build_svn "foo"
+
+ install_gemfile <<-G
+ gem "foo", "1.1", :svn => "file://#{lib_path('foo-1.0')}"
+ G
+
+ expect(out).to include("Source contains 'foo' at: 1.0")
+ end
+
+ it "still works after moving the application directory" do
+ bundle "install --path vendor/bundle"
+ FileUtils.mv bundled_app, tmp('bundled_app.bck')
+
+ Dir.chdir tmp('bundled_app.bck')
+ should_be_installed "foo 1.0"
+ end
+
+ it "can still install after moving the application directory" do
+ bundle "install --path vendor/bundle"
+ FileUtils.mv bundled_app, tmp('bundled_app.bck')
+
+ update_svn "foo", "1.1", :path => lib_path("foo-1.0")
+
+ Dir.chdir tmp('bundled_app.bck')
+ gemfile tmp('bundled_app.bck/Gemfile'), <<-G
+ source "file://#{gem_repo1}"
+ svn "file://#{lib_path('foo-1.0')}" do
+ gem 'foo'
+ end
+
+ gem "rack", "1.0"
+ G
+
+ bundle "update foo"
+
+ should_be_installed "foo 1.1", "rack 1.0"
+ end
+
+ end
+
+ describe "with an empty svn block" do
+ before do
+ build_svn "foo"
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ svn "file://#{lib_path("foo-1.0")}" do
+ # this page left intentionally blank
+ end
+ G
+ end
+
+ it "does not explode" do
+ bundle "install"
+ should_be_installed "rack 1.0"
+ end
+ end
+
+ describe "when specifying a revision" do
+ before(:each) do
+ build_svn "foo"
+ @revision = 1
+ update_svn "foo" do |s|
+ s.write "lib/foo.rb", "puts :CACHE"
+ end
+ end
+
+ it "works" do
+ install_gemfile <<-G
+ svn "file://#{lib_path('foo-1.0')}", :ref => "#{@revision}" do
+ gem "foo"
+ end
+ G
+
+ run <<-RUBY
+ require 'foo'
+ RUBY
+
+ expect(out).not_to eq("CACHE")
+ end
+ end
+
+ describe "when specifying local override" do
+ it "uses the local repository instead of checking a new one out" do
+ # We don't generate it because we actually don't need it
+ # build_svn "rack", "0.8"
+
+ build_svn "rack", "0.8", :path => lib_path('local-rack') do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :svn => "file://#{lib_path('rack-0.8')}"
+ G
+
+ bundle %|config local.rack #{File.join(lib_path('local-rack'), '.checkout')}|
+ bundle :install
+ expect(out).to match(/at #{File.join(lib_path('local-rack'), '.checkout')}/)
+
+ run "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "chooses the local repository on runtime" do
+ build_svn "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path('rack-0.8')}/.", lib_path('local-rack'))
+
+ update_svn "rack", "0.8", :path => lib_path('local-rack') do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :svn => "file://#{lib_path('rack-0.8')}"
+ G
+
+ bundle %|config local.rack #{File.join(lib_path('local-rack'), '.checkout')}|
+ run "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "updates specs on runtime" do
+ system_gems "nokogiri-1.4.2"
+
+ build_svn "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :svn => "file://#{lib_path('rack-0.8')}"
+ G
+
+ lockfile0 = File.read(bundled_app("Gemfile.lock"))
+
+ FileUtils.cp_r("#{lib_path('rack-0.8')}/.", lib_path('local-rack'))
+ update_svn "rack", "0.8", :path => lib_path('local-rack') do |s|
+ s.add_dependency "nokogiri", "1.4.2"
+ end
+
+ bundle %|config local.rack #{File.join(lib_path('local-rack'), '.checkout')}|
+ run "require 'rack'"
+
+ lockfile1 = File.read(bundled_app("Gemfile.lock"))
+ expect(lockfile1).not_to eq(lockfile0)
+ end
+
+ it "updates ref on install" do
+ build_svn "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :svn => "file://#{lib_path('rack-0.8')}"
+ G
+
+ lockfile0 = File.read(bundled_app("Gemfile.lock"))
+
+ FileUtils.cp_r("#{lib_path('rack-0.8')}/.", lib_path('local-rack'))
+ update_svn "rack", "0.8", :path => lib_path('local-rack')
+
+ bundle %|config local.rack #{File.join(lib_path('local-rack'), '.checkout')}|
+ bundle :install
+
+ lockfile1 = File.read(bundled_app("Gemfile.lock"))
+ expect(lockfile1).not_to eq(lockfile0)
+ end
+
+ it "explodes if given path does not exist on install" do
+ build_svn "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :svn => "file://#{lib_path('rack-0.8')}"
+ G
+
+ bundle %|config local.rack #{File.join(lib_path('local-rack'), '.checkout')}|
+ bundle :install
+ expect(out).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(File.join(lib_path('local-rack'), '.checkout').to_s)} does not exist/)
+ end
+ end
+
+ describe "specified inline" do
+ it "installs from svn even if a newer gem is available elsewhere" do
+ build_svn "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :svn => "file://#{lib_path('rack-0.8')}"
+ G
+
+ should_be_installed "rack 0.8"
+ end
+
+ it "installs dependencies from svn even if a newer gem is available elsewhere" do
+ system_gems "rack-1.0.0"
+
+ build_lib "rack", "1.0", :path => lib_path('nested/bar') do |s|
+ s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
+ end
+
+ build_svn "foo", :path => lib_path('nested') do |s|
+ s.add_dependency "rack", "= 1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :svn => "file://#{lib_path('nested')}"
+ G
+
+ run "require 'rack'"
+ expect(out).to eq('WIN OVERRIDE')
+ end
+
+ it "correctly unlocks when changing to a svn source" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ build_svn "rack", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0", :svn => "file://#{lib_path('rack')}"
+ G
+
+ should_be_installed "rack 1.0.0"
+ end
+
+ it "correctly unlocks when changing to a svn source without versions" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ build_svn "rack", "1.2", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :svn => "file://#{lib_path('rack')}"
+ G
+
+ should_be_installed "rack 1.2"
+ end
+ end
+
+ describe "block syntax" do
+ it "pulls all gems from a svn block" do
+ build_lib "omg", :path => lib_path('hi2u/omg')
+ build_lib "hi2u", :path => lib_path('hi2u')
+
+ install_gemfile <<-G
+ path "#{lib_path('hi2u')}" do
+ gem "omg"
+ gem "hi2u"
+ end
+ G
+
+ should_be_installed "omg 1.0", "hi2u 1.0"
+ end
+ end
+
+ it "uses a ref if specified" do
+ build_svn "foo"
+ @revision = 1
+ update_svn "foo" do |s|
+ s.write "lib/foo.rb", "puts :CACHE"
+ end
+
+ install_gemfile <<-G
+ gem "foo", :svn => "file://#{lib_path('foo-1.0')}", :ref => "#{@revision}"
+ G
+
+ run <<-RUBY
+ require 'foo'
+ RUBY
+
+ expect(out).not_to eq("CACHE")
+ end
+
+ it "correctly handles cases with invalid gemspecs" do
+ build_svn "foo" do |s|
+ s.summary = nil
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :svn => "file://#{lib_path('foo-1.0')}"
+ gem "rails", "2.3.2"
+ G
+
+ should_be_installed "foo 1.0"
+ should_be_installed "rails 2.3.2"
+ end
+
+ it "runs the gemspec in the context of its parent directory" do
+ build_lib "bar", :path => lib_path("foo/bar"), :gemspec => false do |s|
+ s.write lib_path("foo/bar/lib/version.rb"), %{BAR_VERSION = '1.0'}
+ s.write "bar.gemspec", <<-G
+ $:.unshift Dir.pwd # For 1.9
+ require 'lib/version'
+ Gem::Specification.new do |s|
+ s.name = 'bar'
+ s.version = BAR_VERSION
+ s.summary = 'Bar'
+ s.files = Dir["lib/**/*.rb"]
+ end
+ G
+ end
+
+ build_svn "foo", :path => lib_path("foo") do |s|
+ s.write "bin/foo", ""
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :svn => "file://#{lib_path("foo")}"
+ gem "rails", "2.3.2"
+ G
+
+ should_be_installed "bar 1.0"
+ should_be_installed "rails 2.3.2"
+ end
+
+ it "installs from svn even if a rubygems gem is present" do
+ build_gem "foo", "1.0", :path => lib_path('fake_foo'), :to_system => true do |s|
+ s.write "lib/foo.rb", "raise 'FAIL'"
+ end
+
+ build_svn "foo", "1.0"
+
+ install_gemfile <<-G
+ gem "foo", "1.0", :svn => "file://#{lib_path('foo-1.0')}"
+ G
+
+ should_be_installed "foo 1.0"
+ end
+
+ it "fakes the gem out if there is no gemspec" do
+ build_svn "foo", :gemspec => false
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", "1.0", :svn => "file://#{lib_path('foo-1.0')}"
+ gem "rails", "2.3.2"
+ G
+
+ should_be_installed("foo 1.0")
+ should_be_installed("rails 2.3.2")
+ end
+
+ it "catches svn errors and spits out useful output" do
+ gemfile <<-G
+ gem "foo", "1.0", :svn => "omgomg"
+ G
+
+ bundle :install, :expect_err => true
+
+ expect(out).to include("SVN error:")
+ expect(err).to include("omgomg")
+ end
+
+ it "doesn't blow up if bundle install is run twice in a row" do
+ build_svn "foo"
+
+ gemfile <<-G
+ gem "foo", :svn => "file://#{lib_path('foo-1.0')}"
+ G
+
+ bundle "install"
+ bundle "install", :exitstatus => true
+ expect(exitstatus).to eq(0)
+ end
+
+ it "does not duplicate svn gem sources" do
+ build_lib "foo", :path => lib_path('nested/foo')
+ build_lib "bar", :path => lib_path('nested/bar')
+
+ build_svn "foo", :path => lib_path('nested')
+ build_svn "bar", :path => lib_path('nested')
+
+ gemfile <<-G
+ gem "foo", :svn => "file://#{lib_path('nested')}"
+ gem "bar", :svn => "file://#{lib_path('nested')}"
+ G
+
+ bundle "install"
+ expect(File.read(bundled_app("Gemfile.lock")).scan('SVN').size).to eq(1)
+ end
+
+ describe "bundle install after the remote has been updated" do
+ it "installs" do
+ build_svn "valim"
+
+ install_gemfile <<-G
+ gem "valim", :svn => "file://#{lib_path("valim-1.0")}"
+ G
+
+ old_revision = "1"
+ update_svn "valim" do |s|
+ s.write "lib/valim.rb", "puts #{old_revision}"
+ end
+ new_revision = "2"
+
+ lockfile = File.read(bundled_app("Gemfile.lock"))
+ File.open(bundled_app("Gemfile.lock"), "w") do |file|
+ file.puts lockfile.gsub(/revision: #{old_revision}/, "revision: #{new_revision}")
+ end
+
+ bundle "install"
+
+ run <<-R
+ require "valim"
+ R
+
+ expect(out).to eq(old_revision)
+ end
+ end
+
+ describe "bundle install --deployment with svn sources" do
+ it "works" do
+ build_svn "valim", :path => lib_path('valim')
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "valim", "= 1.0", :svn => "file://#{lib_path('valim')}"
+ G
+
+ simulate_new_machine
+
+ bundle "install --deployment", :exitstatus => true
+ expect(exitstatus).to eq(0)
+ end
+ end
+
+ describe "gem install hooks" do
+ it "runs pre-install hooks" do
+ build_svn "foo"
+ gemfile <<-G
+ gem "foo", :svn => "file://#{lib_path('foo-1.0')}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ STDERR.puts "Ran pre-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install, :expect_err => true,
+ :requires => [lib_path('install_hooks.rb')]
+ expect(err).to eq("Ran pre-install hook: foo-1.0")
+ end
+
+ it "runs post-install hooks" do
+ build_svn "foo"
+ gemfile <<-G
+ gem "foo", :svn => "file://#{lib_path('foo-1.0')}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.post_install_hooks << lambda do |inst|
+ STDERR.puts "Ran post-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install, :expect_err => true,
+ :requires => [lib_path('install_hooks.rb')]
+ expect(err).to eq("Ran post-install hook: foo-1.0")
+ end
+
+ it "complains if the install hook fails" do
+ build_svn "foo"
+ gemfile <<-G
+ gem "foo", :svn => "file://#{lib_path('foo-1.0')}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ false
+ end
+ H
+ end
+
+ bundle :install, :expect_err => true,
+ :requires => [lib_path('install_hooks.rb')]
+ expect(out).to include("failed for foo-1.0")
+ end
+ end
+
+ context "with an extension" do
+ it "installs the extension" do
+ build_svn "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/foo.rb", "w") do |f|
+ f.puts "FOO = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :svn => "file://#{lib_path('foo-1.0')}"
+ G
+
+ run <<-R
+ require 'foo'
+ puts FOO
+ R
+ expect(out).to eq("YES")
+ end
+ end
+
+ describe "without svn installed" do
+ it "prints a better error message" do
+ build_svn "foo"
+
+ install_gemfile <<-G
+ svn "file://#{lib_path('foo-1.0')}" do
+ gem 'foo'
+ end
+ G
+
+ bundle "update", :env => {"PATH" => ""}
+ expect(out).to include("You need to install svn to be able to use gems from svn repositories. For help installing svn, please refer to SVNook's tutorial at http://svnbook.red-bean.com/en/1.7/svn.intro.install.html")
+ end
+
+ it "installs a packaged svn gem successfully" do
+ build_svn "foo"
+
+ install_gemfile <<-G
+ svn "file://#{lib_path('foo-1.0')}" do
+ gem 'foo'
+ end
+ G
+ bundle "package --all"
+ simulate_new_machine
+
+ bundle "install", :env => {"PATH" => ""}, :exitstatus => true
+ expect(out).to_not include("You need to install svn to be able to use gems from svn repositories.")
+ expect(exitstatus).to be_zero
+ end
+ end
+end
diff --git a/spec/install/gems/dependency_api_spec.rb b/spec/install/gems/dependency_api_spec.rb
index b3e5edf4..863fb142 100644
--- a/spec/install/gems/dependency_api_spec.rb
+++ b/spec/install/gems/dependency_api_spec.rb
@@ -21,7 +21,7 @@ describe "gemcutter's dependency API" do
G
bundle :install, :artifice => "endpoint"
- expect(out).to include("Could not find gem ' sinatra")
+ expect(out).to include("' sinatra' is not a valid gem name because it contains whitespace.")
end
it "should handle nested dependencies" do
@@ -470,6 +470,13 @@ describe "gemcutter's dependency API" do
should_be_installed "rack 1.0.0"
end
+ it "should use the API" do
+ bundle "config #{source_uri}/ #{user}:#{password}"
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ should_be_installed "rack 1.0.0"
+ end
+
it "prefers auth supplied in the source uri" do
gemfile <<-G
source "#{basic_auth_source_uri}"
diff --git a/spec/install/gems/platform_spec.rb b/spec/install/gems/platform_spec.rb
index 71603e10..e226d5c3 100644
--- a/spec/install/gems/platform_spec.rb
+++ b/spec/install/gems/platform_spec.rb
@@ -177,6 +177,32 @@ describe "bundle install with platform conditionals" do
expect(exitstatus).to eq(0)
end
+ it "does not attempt to install gems from :rbx when using --local" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "some_gem", platform: :rbx
+ G
+
+ bundle "install --local"
+ expect(out).not_to match(/Could not find gem 'some_gem/)
+ end
+
+ it "does not attempt to install gems from other rubies when using --local" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+ other_ruby_version_tag = RUBY_VERSION =~ /^1\.8/ ? :ruby_19 : :ruby_18
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "some_gem", platform: :#{other_ruby_version_tag}
+ G
+
+ bundle "install --local"
+ expect(out).not_to match(/Could not find gem 'some_gem/)
+ end
end
describe "when a gem has no architecture" do
diff --git a/spec/lock/lockfile_spec.rb b/spec/lock/lockfile_spec.rb
index ef016c2c..d9700c75 100644
--- a/spec/lock/lockfile_spec.rb
+++ b/spec/lock/lockfile_spec.rb
@@ -207,6 +207,34 @@ describe "the lockfile format" do
G
end
+ it "serializes global svn sources" do
+ build_svn "foo"
+
+ install_gemfile <<-G
+ svn "file://#{lib_path('foo-1.0')}" do
+ gem "foo"
+ end
+ G
+
+ lockfile_should_be <<-G
+ SVN
+ remote: file://#{lib_path('foo-1.0')}
+ revision: 1
+ ref: HEAD
+ specs:
+ foo (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{generic(Gem::Platform.local)}
+
+ DEPENDENCIES
+ foo!
+ G
+ end
+
it "generates a lockfile with a ref for a single pinned source, git gem with a branch requirement" do
git = build_git "foo"
update_git "foo", :branch => "omg"
diff --git a/spec/lock/svn_spec.rb b/spec/lock/svn_spec.rb
new file mode 100644
index 00000000..8c2db1eb
--- /dev/null
+++ b/spec/lock/svn_spec.rb
@@ -0,0 +1,35 @@
+require "spec_helper"
+
+describe "bundle lock with svn gems" do
+ before :each do
+ build_svn "foo"
+
+ install_gemfile <<-G
+ gem 'foo', :svn => "file://#{lib_path('foo-1.0')}"
+ G
+ end
+
+ it "doesn't break right after running lock" do
+ should_be_installed "foo 1.0.0"
+ end
+
+ it "locks a svn source to the current ref" do
+ update_svn "foo" do |s|
+ s.write "lib/foo.rb", "puts :CACHE"
+ end
+ bundle :install
+
+ run <<-RUBY
+ require 'foo'
+ RUBY
+
+ expect(out).not_to eq("CACHE")
+ end
+
+ it "provides correct #full_gem_path" do
+ run <<-RUBY
+ puts Bundler.rubygems.find_name('foo').first.full_gem_path
+ RUBY
+ expect(out).to eq(bundle("show foo"))
+ end
+end
diff --git a/spec/realworld/parallel_spec.rb b/spec/realworld/parallel_spec.rb
index 457139b8..07f12fb7 100644
--- a/spec/realworld/parallel_spec.rb
+++ b/spec/realworld/parallel_spec.rb
@@ -66,4 +66,23 @@ describe "parallel", :realworld => true do
bundle "config jobs"
expect(out).to match(/: "4"/)
end
+
+ it "works with --standalone" do
+ gemfile <<-G, :standalone => true
+ source "https://rubygems.org"
+ gem "diff-lcs"
+ G
+
+ bundle :install, :standalone => true, :jobs => 4
+
+ ruby <<-RUBY, :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "diff/lcs"
+ puts Diff::LCS
+ RUBY
+
+ expect(out).to eq("Diff::LCS")
+ end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 54e79bdc..c70eeff9 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,12 +1,20 @@
$:.unshift File.expand_path('..', __FILE__)
$:.unshift File.expand_path('../../lib', __FILE__)
-# stdlib first
+
+require 'bundler/psyched_yaml'
+require 'fileutils'
require 'uri'
require 'digest/sha1'
-require 'fileutils'
-require 'bundler/psyched_yaml'
-require 'rubygems'
-require 'rspec'
+
+begin
+ require 'rubygems'
+ spec = Gem::Specification.load("bundler.gemspec")
+ gem 'rspec', spec.dependencies.last.requirement.to_s
+ require 'rspec'
+rescue LoadError
+ abort "Run rake spec:deps to install development dependencies"
+end
+
require 'bundler'
# Require the correct version of popen for the current platform
@@ -58,23 +66,8 @@ RSpec.configure do |config|
config.filter_run_excluding :realworld => true
end
- if RUBY_VERSION >= "1.9"
- config.filter_run_excluding :ruby => "1.8"
- else
- config.filter_run_excluding :ruby => "1.9"
- end
-
- if RUBY_VERSION >= "2.0"
- config.filter_run_excluding :ruby => "1.8"
- config.filter_run_excluding :ruby => "1.9"
- else
- config.filter_run_excluding :ruby => "2.0"
- config.filter_run_excluding :ruby => "2.1"
- end
-
- if Gem::VERSION < "2.2"
- config.filter_run_excluding :rubygems => "2.2"
- end
+ config.filter_run_excluding :ruby => LessThanProc.with(RUBY_VERSION)
+ config.filter_run_excluding :rubygems => LessThanProc.with(Gem::VERSION)
config.filter_run :focused => true unless ENV['CI']
config.run_all_when_everything_filtered = true
@@ -107,15 +100,15 @@ RSpec.configure do |config|
Dir.chdir(original_wd)
# Reset ENV
- ENV['PATH'] = original_path
- ENV['GEM_HOME'] = original_gem_home
- ENV['GEM_PATH'] = original_gem_home
- ENV['BUNDLE_PATH'] = nil
- ENV['BUNDLE_GEMFILE'] = nil
- ENV['BUNDLER_TEST'] = nil
- ENV['BUNDLE_FROZEN'] = nil
+ ENV['PATH'] = original_path
+ ENV['GEM_HOME'] = original_gem_home
+ ENV['GEM_PATH'] = original_gem_home
+ ENV['BUNDLE_PATH'] = nil
+ ENV['BUNDLE_GEMFILE'] = nil
+ ENV['BUNDLE_FROZEN'] = nil
+ ENV['BUNDLE_APP_CONFIG'] = nil
+ ENV['BUNDLER_TEST'] = nil
ENV['BUNDLER_SPEC_PLATFORM'] = nil
ENV['BUNDLER_SPEC_VERSION'] = nil
- ENV['BUNDLE_APP_CONFIG'] = nil
end
end
diff --git a/spec/support/builders.rb b/spec/support/builders.rb
index a49cda81..ea2e5db7 100644
--- a/spec/support/builders.rb
+++ b/spec/support/builders.rb
@@ -347,6 +347,17 @@ module Spec
GitReader.new lib_path(spec.full_name)
end
+ def build_svn(name, *args, &block)
+ opts = args.last.is_a?(Hash) ? args.last : {}
+ spec = build_with(SVNBuilder, name, args, &block)
+ SVNReader.new(opts[:path] || lib_path(spec.full_name))
+ end
+
+ def update_svn(name, *args, &block)
+ spec = build_with(SVNUpdater, name, args, &block)
+ SVNReader.new lib_path(spec.full_name)
+ end
+
private
def build_with(builder, name, args, &blk)
@@ -599,6 +610,62 @@ module Spec
end
+ class SVNBuilder < LibBuilder
+ def _build(options)
+ path = options[:path] || _default_path
+ checkout_path = File.join(path, '.checkout')
+ super(options.merge(:path => path))
+ Dir.chdir(path) do
+ `mkdir .repo_data && find . -maxdepth 1 ! \\( -name ".repo_data" -or -name "." \\) -exec mv {} \\.repo_data/ \\;`
+ `svnadmin create .repo`
+ `svn import .repo_data file://#{File.join(path, '.repo')} -m 'OMG INITIAL COMMIT'`
+ `mv .repo/* .`
+ end
+
+ `mkdir #{checkout_path}`
+ Dir.chdir(checkout_path) do
+ `svn checkout file://#{path} .`
+ end
+ end
+ end
+
+ class SVNUpdater < LibBuilder
+ def _build(options)
+ path = options[:path] || _default_path
+ checkout_path = File.join(path, '.checkout')
+
+ Dir.chdir(checkout_path) do
+ current_ref = `svn info --revision HEAD file://#{checkout_path} | grep \"Revision\" | awk '{print $2}'`.strip
+ _default_files.keys.each do |prev_ref_path|
+ _default_files[prev_ref_path] << "\n#{Builders.constantize(name)}_PREV_REF = '#{current_ref}'"
+ end
+ super(options.merge(:path => checkout_path, :gemspec => false))
+ `svn add --force *`
+ `svn commit -m "BUMP"`
+ end
+ end
+ end
+
+ class SVNReader
+ attr_reader :path
+
+ def initialize(path)
+ @path = path
+ end
+
+ def ref_for(ref, len = nil)
+ ref = svn("info --revision #{ref} file://#{path} | grep \"Revision\" | awk '{print $2}'").strip
+ ref = ref[0..len] if len
+ ref
+ end
+
+ private
+
+ def svn(cmd)
+ Dir.chdir(@path) { `svn #{cmd}`.strip }
+ end
+ end
+
class GemBuilder < LibBuilder
def _build(opts)
diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb
index db1a785e..1c3d2dcf 100644
--- a/spec/support/helpers.rb
+++ b/spec/support/helpers.rb
@@ -4,10 +4,10 @@ module Spec
@in_p, @out_p, @err_p = nil, nil, nil
Dir["#{tmp}/{gems/*,*}"].each do |dir|
next if %(base remote1 gems rubygems).include?(File.basename(dir))
- unless ENV['BUNDLER_SUDO_TESTS']
- FileUtils.rm_rf(dir)
- else
+ if ENV['BUNDLER_SUDO_TESTS']
`sudo rm -rf #{dir}`
+ else
+ FileUtils.rm_rf(dir)
end
end
FileUtils.mkdir_p(tmp)
@@ -163,8 +163,8 @@ module Spec
config
end
- def gemfile(*args)
- path = bundled_app("Gemfile")
+ def create_file(*args)
+ path = bundled_app(args.shift)
path = args.shift if args.first.is_a?(Pathname)
str = args.shift || ""
path.dirname.mkpath
@@ -173,13 +173,16 @@ module Spec
end
end
+ def gemfile(*args)
+ create_file("Gemfile", *args)
+ end
+
def lockfile(*args)
- path = bundled_app("Gemfile.lock")
- path = args.shift if args.first.is_a?(Pathname)
- str = args.shift || ""
- File.open(path.to_s, 'w') do |f|
- f.puts strip_whitespace(str)
- end
+ create_file("Gemfile.lock", *args)
+ end
+
+ def consolerc(*args)
+ create_file(".consolerc", *args)
end
def strip_whitespace(str)
diff --git a/spec/support/less_than_proc.rb b/spec/support/less_than_proc.rb
new file mode 100644
index 00000000..af80d82c
--- /dev/null
+++ b/spec/support/less_than_proc.rb
@@ -0,0 +1,14 @@
+class LessThanProc < Proc
+ attr_accessor :present
+
+ def self.with(present)
+ pv = Gem::Version.new(present.dup)
+ lt = self.new { |required| pv < Gem::Version.new(required) }
+ lt.present = present
+ return lt
+ end
+
+ def inspect
+ "\"=< #{present.to_s}\""
+ end
+end
diff --git a/spec/update/svn_spec.rb b/spec/update/svn_spec.rb
new file mode 100644
index 00000000..67f9dca2
--- /dev/null
+++ b/spec/update/svn_spec.rb
@@ -0,0 +1,100 @@
+require "spec_helper"
+
+describe "bundle update" do
+ describe "svn sources" do
+ it "updates correctly when you have like craziness" do
+ build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
+ build_svn "rails", "3.0", :path => lib_path("rails") do |s|
+ s.add_dependency "activesupport", "= 3.0"
+ end
+
+ install_gemfile <<-G
+ gem "rails", :svn => "file://#{lib_path('rails')}"
+ G
+
+ bundle "update rails"
+ expect(out).to include("Using activesupport 3.0 from file://#{lib_path('rails')} (at HEAD)")
+ should_be_installed "rails 3.0", "activesupport 3.0"
+ end
+
+ it "floats on master when updating all gems that are pinned to the source even if you have child dependencies" do
+ build_svn "foo", :path => lib_path('foo')
+ build_gem "bar", :to_system => true do |s|
+ s.add_dependency "foo"
+ end
+
+ install_gemfile <<-G
+ gem "foo", :svn => "file://#{lib_path('foo')}"
+ gem "bar"
+ G
+
+ update_svn "foo", :path => lib_path('foo') do |s|
+ s.write "lib/foo.rb", "FOO = '1.1'"
+ end
+
+ bundle "update foo"
+
+ should_be_installed "foo 1.1"
+ end
+
+ it "notices when you change the repo url in the Gemfile" do
+ build_svn "foo", :path => lib_path("foo_one")
+ build_svn "foo", :path => lib_path("foo_two")
+
+ install_gemfile <<-G
+ gem "foo", "1.0", :svn => "file://#{lib_path('foo_one')}"
+ G
+
+ FileUtils.rm_rf lib_path("foo_one")
+
+ install_gemfile <<-G
+ gem "foo", "1.0", :svn => "file://#{lib_path('foo_two')}"
+ G
+
+ expect(err).to be_empty
+ expect(out).to include("Fetching file://#{lib_path}/foo_two")
+ expect(out).to include("Your bundle is complete!")
+ end
+
+ it "should not explode on invalid revision on update of gem by name" do
+ build_svn "rack", "0.8"
+
+ build_svn "rack", "0.8", :path => lib_path('local-rack') do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :svn => "file://#{lib_path('rack-0.8')}", :branch => "master"
+ G
+
+ bundle %|config local.rack #{File.join(lib_path('local-rack'), '.checkout')}|
+ bundle "update rack"
+ expect(out).to include("Your bundle is updated!")
+ end
+
+ it "shows the previous version of the gem" do
+ build_svn "rails", "3.0", :path => lib_path("rails")
+
+ install_gemfile <<-G
+ gem "rails", :svn => "file://#{lib_path('rails')}"
+ G
+
+ lockfile <<-G
+ SVN
+ remote: file://#{lib_path("rails")}
+ specs:
+ rails (2.3.2)
+
+ PLATFORMS
+ #{generic(Gem::Platform.local)}
+
+ DEPENDENCIES
+ rails!
+ G
+
+ bundle "update"
+ expect(out).to include("Using rails 3.0 (was 2.3.2) from file://#{lib_path('rails')} (at HEAD)")
+ end
+ end
+end