aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-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
42 files changed, 909 insertions, 448 deletions
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