Skip to content
This repository has been archived by the owner on Apr 14, 2021. It is now read-only.

Commit

Permalink
Path and Git should also be packaged.
Browse files Browse the repository at this point in the history
  • Loading branch information
José Valim committed Mar 20, 2012
1 parent 9ccaf91 commit 6ef6e75
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 65 deletions.
157 changes: 96 additions & 61 deletions lib/bundler/source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
require "rubygems/spec_fetcher"
require "rubygems/format"
require "digest/sha1"
require "open3"
require "fileutils"

module Bundler
module Source
Expand Down Expand Up @@ -264,12 +264,37 @@ def remote_specs
Bundler.rubygems.sources = old
end
end

end


class Path
attr_reader :path, :options
# Kind of a hack, but needed for the lock file parser
class Installer < Bundler::GemInstaller
def initialize(spec, options = {})
@spec = spec
@bin_dir = Bundler.requires_sudo? ? "#{Bundler.tmp}/bin" : "#{Bundler.rubygems.gem_dir}/bin"
@gem_dir = Bundler.rubygems.path(spec.full_gem_path)
@wrappers = options[:wrappers] || true
@env_shebang = options[:env_shebang] || true
@format_executable = options[:format_executable] || false
end

def generate_bin
return if spec.executables.nil? || spec.executables.empty?

if Bundler.requires_sudo?
FileUtils.mkdir_p("#{Bundler.tmp}/bin") unless File.exist?("#{Bundler.tmp}/bin")
end
super
if Bundler.requires_sudo?
Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/bin"
spec.executables.each do |exe|
Bundler.sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Bundler.rubygems.gem_dir}/bin/"
end
end
end
end

attr_reader :path, :options
attr_writer :name
attr_accessor :version

Expand All @@ -287,8 +312,9 @@ def initialize(options)
@path = @path.expand_path(Bundler.root) unless @path.relative?
end

@name = options["name"]
@name = options["name"]
@version = options["version"]
@path = app_cache_path if has_app_cache?
end

def remote!
Expand Down Expand Up @@ -330,9 +356,40 @@ def name
File.basename(path.expand_path(Bundler.root).to_s)
end

def install(spec)
Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
# Let's be honest, when we're working from a path, we can't
# really expect native extensions to work because the whole point
# is to just be able to modify what's in that path and go. So, let's
# not put ourselves through the pain of actually trying to generate
# the full gem.
Installer.new(spec).generate_bin
end

def cache(spec)
return if path.expand_path(Bundler.root).to_s.index(Bundler.root.to_s) == 0
FileUtils.rm_rf(app_cache_path)
FileUtils.cp_r("#{path}/.", app_cache_path)
end

def local_specs(*)
@local_specs ||= load_spec_files
end

alias :specs :local_specs

private

def app_cache_path
@app_cache_path ||= Bundler.app_cache.join(name)
end

def has_app_cache?
SharedHelpers.in_bundle? && app_cache_path.exist?
end

def load_spec_files
index = Index.new

expanded_path = path.expand_path(Bundler.root)

if File.directory?(expanded_path)
Expand Down Expand Up @@ -368,61 +425,10 @@ def load_spec_files
index
end

def local_specs(*)
@local_specs ||= load_spec_files
end

class Installer < Bundler::GemInstaller
def initialize(spec, options = {})
@spec = spec
@bin_dir = Bundler.requires_sudo? ? "#{Bundler.tmp}/bin" : "#{Bundler.rubygems.gem_dir}/bin"
@gem_dir = Bundler.rubygems.path(spec.full_gem_path)
@wrappers = options[:wrappers] || true
@env_shebang = options[:env_shebang] || true
@format_executable = options[:format_executable] || false
end

def generate_bin
return if spec.executables.nil? || spec.executables.empty?

if Bundler.requires_sudo?
FileUtils.mkdir_p("#{Bundler.tmp}/bin") unless File.exist?("#{Bundler.tmp}/bin")
end
super
if Bundler.requires_sudo?
Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/bin"
spec.executables.each do |exe|
Bundler.sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Bundler.rubygems.gem_dir}/bin/"
end
end
end
end

def install(spec)
Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
# Let's be honest, when we're working from a path, we can't
# really expect native extensions to work because the whole point
# is to just be able to modify what's in that path and go. So, let's
# not put ourselves through the pain of actually trying to generate
# the full gem.
Installer.new(spec).generate_bin
end

alias specs local_specs

def cache(spec)
unless path.expand_path(Bundler.root).to_s.index(Bundler.root.to_s) == 0
Bundler.ui.warn " * #{spec.name} at `#{path}` will not be cached."
end
end

private

def relative_path
if path.to_s.match(%r{^#{Bundler.root.to_s}})
return path.relative_path_from(Bundler.root)
end

path
end

Expand All @@ -442,7 +448,7 @@ def generate_bin(spec)

gem_file = Dir.chdir(gem_dir){ Gem::Builder.new(spec).build }

installer = Installer.new(spec, :env_shebang => false)
installer = Path::Installer.new(spec, :env_shebang => false)
run_hooks(:pre_install, installer)
installer.build_extensions
run_hooks(:post_build, installer)
Expand Down Expand Up @@ -617,17 +623,29 @@ def allowed_in_path
attr_reader :uri, :ref, :options, :submodules

def initialize(options)
super
@options = options
@glob = options["glob"] || DEFAULT_GLOB

# stringify options that could be set as symbols
@allow_cached = false
@allow_remote = false

# Stringify options that could be set as symbols
%w(ref branch tag revision).each{|k| options[k] = options[k].to_s if options[k] }

@uri = options["uri"]
@ref = options["ref"] || options["branch"] || options["tag"] || 'master'
@submodules = options["submodules"]
@name = options["name"]
@version = options["version"]

@update = false
@installed = nil
@local = false

if has_app_cache?
@local = true
@install_path = @cache_path = app_cache_path
end
end

def self.from_lock(options)
Expand Down Expand Up @@ -690,6 +708,8 @@ def unlock!
end

def local_override!(path)
return false if local?

path = Pathname.new(path)
path = path.expand_path(Bundler.root) unless path.relative?

Expand Down Expand Up @@ -745,6 +765,14 @@ def install(spec)
generate_bin(spec)
end

def cache(spec)
return if path.expand_path(Bundler.root).to_s.index(Bundler.root.to_s) == 0
FileUtils.rm_rf(app_cache_path)
git_proxy.checkout
git_proxy.copy_to(app_cache_path, @submodules)
FileUtils.rm_rf(app_cache_path.join(".git"))
end

def load_spec_files
super
rescue PathError, GitError
Expand All @@ -765,6 +793,14 @@ def cache_path

private

def has_app_cache?
cached_revision && super
end

def app_cache_path
@app_cache_path ||= Bundler.app_cache.join("#{base_name}-#{shortref_for_path(revision)}")
end

def local?
@local
end
Expand Down Expand Up @@ -817,6 +853,5 @@ def git_proxy
@git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision){ allow_git_ops? }
end
end

end
end
68 changes: 67 additions & 1 deletion spec/cache/git_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "spec_helper"

describe "bundle cache with git" do
it "base_name should strip private repo uris" do
source = Bundler::Source::Git.new("uri" => "git@github.com:bundler.git")
Expand All @@ -9,4 +10,69 @@
source = Bundler::Source::Git.new("uri" => "//MachineName/ShareFolder")
source.send(:base_name).should == "ShareFolder"
end
end

it "copies repository to vendor cache" do
git = build_git "foo"
ref = git.ref_for("master", 11)

install_gemfile <<-G
gem "foo", :git => '#{lib_path("foo-1.0")}'
G

bundle "cache"
bundled_app("vendor/cache/foo-1.0-#{ref}").should exist
bundled_app("vendor/cache/foo-1.0-#{ref}/.git").should_not exist

FileUtils.rm_rf lib_path("foo-1.0")
out.should == "Updating .gem files in vendor/cache"
should_be_installed "foo 1.0"
end

it "ignores local repository in favor of the cache" do
git = build_git "foo"
ref = git.ref_for("master", 11)

build_git "foo", :path => lib_path('local-foo') do |s|
s.write "lib/foo.rb", "raise :FAIL"
end

install_gemfile <<-G
gem "foo", :git => '#{lib_path("foo-1.0")}', :branch => :master
G

bundle "cache"
bundle %|config local.foo #{lib_path('local-foo')}|

bundle :install
out.should =~ /at #{bundled_app("vendor/cache/foo-1.0-#{ref}")}/

should_be_installed "foo 1.0"
end

it "copies repository to vendor cache, including submodules" do
build_git "submodule", "1.0"

git = build_git "has_submodule", "1.0" do |s|
s.add_dependency "submodule"
end

Dir.chdir(lib_path('has_submodule-1.0')) do
`git submodule add #{lib_path('submodule-1.0')} submodule-1.0`
`git commit -m "submodulator"`
end

install_gemfile <<-G
git "#{lib_path('has_submodule-1.0')}", :submodules => true do
gem "has_submodule"
end
G

ref = git.ref_for("master", 11)
bundle "cache"

bundled_app("vendor/cache/has_submodule-1.0-#{ref}").should exist
bundled_app("vendor/cache/has_submodule-1.0-#{ref}/submodule-1.0").should exist
out.should == "Updating .gem files in vendor/cache"
should_be_installed "has_submodule 1.0"
end
end
13 changes: 10 additions & 3 deletions spec/cache/path_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,33 @@

describe "bundle cache" do
describe "with path sources" do
it "is silent when the path is within the bundle" do
it "is no-op when the path is within the bundle" do
build_lib "foo", :path => bundled_app("lib/foo")

install_gemfile <<-G
gem "foo", :path => '#{bundled_app("lib/foo")}'
G

bundle "cache"
bundled_app("vendor/cache/foo-1.0").should_not exist

out.should == "Updating .gem files in vendor/cache"
should_be_installed "foo 1.0"
end

it "warns when the path is outside of the bundle" do
it "copies when the path is outside the bundle " do
build_lib "foo"

install_gemfile <<-G
gem "foo", :path => '#{lib_path("foo-1.0")}'
G

bundle "cache"
out.should include("foo at `#{lib_path("foo-1.0")}` will not be cached")
bundled_app("vendor/cache/foo-1.0").should exist

FileUtils.rm_rf lib_path("foo-1.0")
out.should == "Updating .gem files in vendor/cache"
should_be_installed "foo 1.0"
end
end
end

5 comments on commit 6ef6e75

@davidw
Copy link

@davidw davidw commented on 6ef6e75 Sep 5, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This broke my code, which used Open3 without doing a require.

@josevalim
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What a wonderful opportunity to fix your code then!

@josevalim
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, thanks for reporting. But I really think your code is the one to blame this time. :) It is a bad practice to depend on other code requires.

@davidw
Copy link

@davidw davidw commented on 6ef6e75 Sep 5, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I felt it were a bug with bundler I would have opened a bug report. I just needed to say something somewhere after tracking down why the heck things weren't working any more, even though the fix was pretty obvious!

@josevalim
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured it out later. I replied too fast because I didn't want to miss the joke, hehe, sorry! ❤️

Please sign in to comment.