diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 49fb808de12..c79b8983a0d 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -6,19 +6,22 @@ def self.run(command, options = {}) new(options).run(command) rescue DefaultManifestNotFound => e Bundler.logger.error "Could not find a Gemfile to use" - exit 2 + exit 3 rescue InvalidEnvironmentName => e Bundler.logger.error "Gemfile error: #{e.message}" - exit + exit 4 rescue InvalidRepository => e Bundler.logger.error e.message - exit 1 + exit 5 rescue VersionConflict => e Bundler.logger.error e.message - exit 1 + exit 6 rescue GemNotFound => e Bundler.logger.error e.message - exit 1 + exit 7 + rescue InvalidCacheArgument => e + Bundler.logger.error e.message + exit 8 end def initialize(options) @@ -30,6 +33,10 @@ def bundle @manifest.install(@options) end + def cache + @manifest.cache(@options) + end + def exec @manifest.setup_environment # w0t? diff --git a/lib/bundler/commands/bundle_command.rb b/lib/bundler/commands/bundle_command.rb index f0af14e4432..4da1b3172c4 100644 --- a/lib/bundler/commands/bundle_command.rb +++ b/lib/bundler/commands/bundle_command.rb @@ -14,6 +14,10 @@ def initialize add_option('--cached', "Only use cached gems when expanding the bundle") do options[:cached] = true end + + add_option('--cache GEM', "Specify a path to a .gem file to add to the bundled gem cache") do |gem, options| + options[:cache] = gem + end end def usage @@ -29,7 +33,11 @@ def description # :nodoc: def execute # Prevent the bundler from getting required unless it is actually being used require 'bundler' - Bundler::CLI.run(:bundle, options) + if options[:cache] + Bundler::CLI.run(:cache, options) + else + Bundler::CLI.run(:bundle, options) + end end end \ No newline at end of file diff --git a/lib/bundler/environment.rb b/lib/bundler/environment.rb index e069fce5ff0..112ad7fa675 100644 --- a/lib/bundler/environment.rb +++ b/lib/bundler/environment.rb @@ -2,6 +2,7 @@ module Bundler class DefaultManifestNotFound < StandardError; end + class InvalidCacheArgument < StandardError; end class Environment attr_reader :filename, :dependencies @@ -58,6 +59,36 @@ def install(options = {}) Bundler.logger.info "Done." end + def cache(options = {}) + gemfile = options[:cache] + + if File.extname(gemfile) == ".gem" + if !File.exist?(gemfile) + raise InvalidCacheArgument, "'#{gemfile}' does not exist." + end + repository.cache(gemfile) + elsif File.directory?(gemfile) || gemfile.include?('/') + if !File.directory?(gemfile) + raise InvalidCacheArgument, "'#{gemfile}' does not exist." + end + gemfiles = Dir["#{gemfile}/*.gem"] + if gemfiles.empty? + raise InvalidCacheArgument, "'#{gemfile}' contains no gemfiles" + end + repository.cache(*gemfiles) + else + local = Gem::SourceIndex.from_installed_gems.find_name(gemfile).last + + if !local + raise InvalidCacheArgument, "w0t? '#{gemfile}' means nothing to me." + end + + gemfile = Pathname.new(local.loaded_from) + gemfile = gemfile.dirname.join('..', 'cache', "#{local.full_name}.gem").expand_path + repository.cache(File.join(Gem.dir, "cache", "#{local.full_name}.gem")) + end + end + def setup_environment unless system_gems ENV["GEM_HOME"] = gem_path diff --git a/lib/bundler/repository.rb b/lib/bundler/repository.rb index 3094f2954c8..edbcf5083e5 100644 --- a/lib/bundler/repository.rb +++ b/lib/bundler/repository.rb @@ -38,6 +38,14 @@ def install(dependencies, sources, options = {}) configure(valid, options) end + def cache(*gemfiles) + FileUtils.mkdir_p(@path.join("cache")) + gemfiles.each do |gemfile| + Bundler.logger.info "Caching: #{File.basename(gemfile)}" + FileUtils.cp(gemfile, @path.join("cache")) + end + end + def gems source_index.gems.values end diff --git a/spec/bundler/cli_spec.rb b/spec/bundler/cli_spec.rb index 73b9e9c7d05..45df2eaec03 100644 --- a/spec/bundler/cli_spec.rb +++ b/spec/bundler/cli_spec.rb @@ -260,4 +260,83 @@ end end end + + describe "caching gems to the bundle" do + before(:each) do + build_manifest <<-Gemfile + clear_sources + Gemfile + end + + it "adds a single gem to the cache" do + build_manifest <<-Gemfile + clear_sources + gem "rack" + Gemfile + + Dir.chdir(bundled_app) do + out = gem_command :bundle, "--cache #{gem_repo1('gems', 'rack-0.9.1.gem')}" + gem_command :bundle, "--cached" + out.should include("Caching: rack-0.9.1.gem") + tmp_gem_path.should include_cached_gems("rack-0.9.1") + tmp_gem_path.should include_installed_gems("rack-0.9.1") + end + end + + it "adds a gem directory to the cache" do + build_manifest <<-Gemfile + clear_sources + gem "rack" + gem "abstract" + Gemfile + + Dir.chdir(bundled_app) do + out = gem_command :bundle, "--cache #{gem_repo1('gems')}" + gem_command :bundle, "--cached" + + %w(abstract-1.0.0 actionmailer-2.3.2 activerecord-2.3.2 addressable-2.0.2 builder-2.1.2).each do |gemfile| + out.should include("Caching: #{gemfile}.gem") + end + tmp_gem_path.should include_cached_gems("rack-0.9.1", "abstract-1.0.0") + tmp_gem_path.should have_installed_gems("rack-0.9.1", "abstract-1.0.0") + end + end + + it "adds a gem from the local repository" do + build_manifest <<-Gemfile + clear_sources + gem "rspec" + disable_system_gems + Gemfile + + Dir.chdir(bundled_app) do + out = gem_command :bundle, "--cache rspec" + gem_command :bundle, "--cached" + out = run_in_context "require 'spec' ; puts Spec" + out.should == "Spec" + end + end + + it "outputs an error when trying to cache a gem that doesn't exist." do + Dir.chdir(bundled_app) do + out = gem_command :bundle, "--cache foo/bar.gem" + out.should == "'foo/bar.gem' does not exist." + end + end + + it "outputs an error when trying to cache a directory that doesn't exist." do + Dir.chdir(bundled_app) do + out = gem_command :bundle, "--cache foo/bar" + out.should == "'foo/bar' does not exist." + end + end + + it "outputs an error when trying to cache a directory with no gems." do + Dir.chdir(bundled_app) do + FileUtils.mkdir_p("foo/bar") + out = gem_command :bundle, "--cache foo/bar" + out.should == "'foo/bar' contains no gemfiles" + end + end + end end diff --git a/spec/support/path_utils.rb b/spec/support/path_utils.rb index 3244fb68fdb..0b71ccf6975 100644 --- a/spec/support/path_utils.rb +++ b/spec/support/path_utils.rb @@ -40,16 +40,16 @@ def fixture_dir root.join("spec", "fixtures") end - def gem_repo1 - fixture_dir.join("repository1").expand_path + def gem_repo1(*path) + fixture_dir.join("repository1", *path).expand_path end - def gem_repo2 - fixture_dir.join("repository2").expand_path + def gem_repo2(*path) + fixture_dir.join("repository2", *path).expand_path end - def gem_repo3 - fixture_dir.join("repository3").expand_path + def gem_repo3(*path) + fixture_dir.join("repository3", *path).expand_path end def fixture(gem_name)