bundler08 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.markdown +284 -0
- data/Rakefile +81 -0
- data/lib/bundler08.rb +49 -0
- data/lib/bundler08/bundle.rb +314 -0
- data/lib/bundler08/cli.rb +89 -0
- data/lib/bundler08/commands/bundle_command.rb +83 -0
- data/lib/bundler08/commands/exec_command.rb +36 -0
- data/lib/bundler08/dependency.rb +62 -0
- data/lib/bundler08/dsl.rb +182 -0
- data/lib/bundler08/environment.rb +87 -0
- data/lib/bundler08/finder.rb +51 -0
- data/lib/bundler08/gem_bundle.rb +11 -0
- data/lib/bundler08/gem_ext.rb +34 -0
- data/lib/bundler08/remote_specification.rb +53 -0
- data/lib/bundler08/resolver.rb +250 -0
- data/lib/bundler08/runtime.rb +2 -0
- data/lib/bundler08/source.rb +365 -0
- data/lib/bundler08/templates/Gemfile +72 -0
- data/lib/bundler08/templates/app_script.erb +3 -0
- data/lib/bundler08/templates/environment.erb +156 -0
- data/lib/bundler08/templates/environment_picker.erb +4 -0
- data/lib/rubygems_plugin.rb +6 -0
- metadata +81 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Engine Yard
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
## Bundler : A gem to bundle gems
|
2
|
+
|
3
|
+
Github: http://github.com/wycats/bundler
|
4
|
+
Mailing list: http://groups.google.com/group/ruby-bundler
|
5
|
+
IRC: #carlhuda on freenode
|
6
|
+
|
7
|
+
## Intro
|
8
|
+
|
9
|
+
Bundler is a tool that manages gem dependencies for your ruby application. It
|
10
|
+
takes a gem manifest file and is able to fetch, download, and install the gems
|
11
|
+
and all child dependencies specified in this manifest. It can manage any update
|
12
|
+
to the gem manifest file and update the bundled gems accordingly. It also lets
|
13
|
+
you run any ruby code in context of the bundled gem environment.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Bundler has no dependencies. Just clone the git repository and install the gem
|
18
|
+
with the following rake task:
|
19
|
+
|
20
|
+
rake install
|
21
|
+
|
22
|
+
You can also install the gem with
|
23
|
+
|
24
|
+
gem install bundler
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
Bundler requires a gem manifest file to be created. This should be a file named
|
29
|
+
`Gemfile` located in the root directory of your application. After the manifest
|
30
|
+
has been created, in your shell, cd into your application's directory and run
|
31
|
+
`gem bundle`. This will start the bundling process.
|
32
|
+
|
33
|
+
### Manifest file
|
34
|
+
|
35
|
+
This is where you specify all of your application's dependencies. By default
|
36
|
+
this should be in a file named `Gemfile` located in your application's root
|
37
|
+
directory. The following is an example of a potential `Gemfile`. For more
|
38
|
+
information, please refer to Bundler::ManifestBuilder.
|
39
|
+
|
40
|
+
# Specify a dependency on rails. When the bundler downloads gems,
|
41
|
+
# it will download rails as well as all of rails' dependencies (such as
|
42
|
+
# activerecord, actionpack, etc...)
|
43
|
+
#
|
44
|
+
# At least one dependency must be specified
|
45
|
+
gem "rails"
|
46
|
+
|
47
|
+
# Specify a dependency on rack v.1.0.0. The version is optional. If present,
|
48
|
+
# it can be specified the same way as with rubygems' #gem method.
|
49
|
+
gem "rack", "1.0.0"
|
50
|
+
|
51
|
+
# Specify a dependency rspec, but only require that gem in the "testing"
|
52
|
+
# environment. :except is also a valid option to specify environment
|
53
|
+
# restrictions.
|
54
|
+
gem "rspec", :only => :testing
|
55
|
+
|
56
|
+
# Specify a dependency, but specify that it is already present and expanded
|
57
|
+
# at vendor/rspec. Bundler will treat rspec as though it was the rspec gem
|
58
|
+
# for the purpose of gem resolution: if another gem depends on a version
|
59
|
+
# of rspec satisfied by "1.1.6", it will be used.
|
60
|
+
#
|
61
|
+
# If a gemspec is found in the directory, it will be used to specify load
|
62
|
+
# paths and supply additional dependencies.
|
63
|
+
#
|
64
|
+
# Bundler will also recursively search for *.gemspec, and assume that
|
65
|
+
# gemspecs it finds represent gems that are rooted in the same directory
|
66
|
+
# the gemspec is found in.
|
67
|
+
gem "rspec", "1.1.6", :vendored_at => "vendor/rspec"
|
68
|
+
|
69
|
+
# You can also control what will happen when you run Bundler.require_env
|
70
|
+
# by using the :require_as option, as per the next two examples.
|
71
|
+
|
72
|
+
# Don't auto-require this gem.
|
73
|
+
gem "rspec-rails", "1.2.9", :require_as => nil
|
74
|
+
|
75
|
+
# Require something other than the default.
|
76
|
+
gem "yajl-ruby", "0.6.7", :require_as => "yajl/json_gem"
|
77
|
+
|
78
|
+
# Works exactly like :vendored_at, but first downloads the repo from
|
79
|
+
# git and handles stashing the files for you. As with :vendored_at,
|
80
|
+
# Bundler will automatically use *.gemspec files in the root or anywhere
|
81
|
+
# in the repository.
|
82
|
+
gem "rails", "3.0.pre", :git => "git://github.com/rails/rails.git"
|
83
|
+
|
84
|
+
# Add http://gems.github.com as a source that the bundler will use
|
85
|
+
# to find gems listed in the manifest. By default,
|
86
|
+
# http://gems.rubyforge.org is already added to the list.
|
87
|
+
#
|
88
|
+
# This is an optional setting.
|
89
|
+
source "http://gems.github.com"
|
90
|
+
|
91
|
+
# Specify where the bundled gems should be stashed. This directory will
|
92
|
+
# be a gem repository where all gems are downloaded to and installed to.
|
93
|
+
#
|
94
|
+
# This is an optional setting.
|
95
|
+
# The default is: vendor/gems
|
96
|
+
bundle_path "my/bundled/gems"
|
97
|
+
|
98
|
+
# Specify where gem executables should be copied to.
|
99
|
+
#
|
100
|
+
# This is an optional setting.
|
101
|
+
# The default is: bin
|
102
|
+
bin_path "my/executables"
|
103
|
+
|
104
|
+
# Specify that rubygems should be completely disabled. This means that it
|
105
|
+
# will be impossible to require it and that available gems will be
|
106
|
+
# limited exclusively to gems that have been bundled.
|
107
|
+
#
|
108
|
+
# The default is to automatically require rubygems. There is also a
|
109
|
+
# `disable_system_gems` option that will limit available rubygems to
|
110
|
+
# the ones that have been bundled.
|
111
|
+
disable_rubygems
|
112
|
+
|
113
|
+
### Gem Resolution
|
114
|
+
|
115
|
+
One of the most important things that the bundler does is do a
|
116
|
+
dependency resolution on the full list of gems that you specify, all
|
117
|
+
at once. This differs from the one-at-a-time dependency resolution that
|
118
|
+
Rubygems does, which can result in the following problem:
|
119
|
+
|
120
|
+
# On my system:
|
121
|
+
# activesupport 3.0.pre
|
122
|
+
# activesupport 2.3.4
|
123
|
+
# activemerchant 1.4.2
|
124
|
+
# rails 2.3.4
|
125
|
+
#
|
126
|
+
# activemerchant 1.4.2 depends on activesupport >= 2.3.2
|
127
|
+
|
128
|
+
gem "activemerchant", "1.4.2"
|
129
|
+
# results in activating activemerchant, as well as
|
130
|
+
# activesupport 3.0.pre, since it is >= 2.3.2
|
131
|
+
|
132
|
+
gem "rails", "2.3.4"
|
133
|
+
# results in:
|
134
|
+
# can't activate activesupport (= 2.3.4, runtime)
|
135
|
+
# for ["rails-2.3.4"], already activated
|
136
|
+
# activesupport-3.0.pre for ["activemerchant-1.4.2"]
|
137
|
+
|
138
|
+
This is because activemerchant has a broader dependency, which results
|
139
|
+
in the activation of a version of activesupport that does not satisfy
|
140
|
+
a more narrow dependency.
|
141
|
+
|
142
|
+
Bundler solves this problem by evaluating all dependencies at once,
|
143
|
+
so it can detect that all gems *together* require activesupport "2.3.4".
|
144
|
+
|
145
|
+
### Running Bundler
|
146
|
+
|
147
|
+
Once a manifest file has been created, the only thing that needs to be done
|
148
|
+
is to run the `gem bundle` command anywhere in your application. The script
|
149
|
+
will load the manifest file, resolve all the dependencies, download all
|
150
|
+
needed gems, and install them into the specified directory.
|
151
|
+
|
152
|
+
Every time an update is made to the manifest file, run `gem bundle` again to
|
153
|
+
get the changes installed. This will only check the remote sources if your
|
154
|
+
currently installed gems do not satisfy the `Gemfile`. If you want to force
|
155
|
+
checking for updates on the remote sources, use the `--update` option.
|
156
|
+
|
157
|
+
### Remote deploys
|
158
|
+
|
159
|
+
When you run `gem bundle`, the following steps occur:
|
160
|
+
|
161
|
+
1. Gemfile is read in
|
162
|
+
2. The gems specified in the Gemfile are resolved against the gems
|
163
|
+
already in your bundle. If the dependencies resolve, skip to step 5.
|
164
|
+
3. If the dependencies in your Gemfile cannot be fully resolved
|
165
|
+
against the gems already in the bundle, the metadata for each
|
166
|
+
source is fetched.
|
167
|
+
4. The gems in the Gemfile are resolved against the full list of
|
168
|
+
available gems in all sources, and the resulting gems are downloaded
|
169
|
+
5. Each gem that has been downloaded but not yet expanded is expanded
|
170
|
+
into the local directory. This expansion process also installs
|
171
|
+
native gems.
|
172
|
+
|
173
|
+
As you can see, if you run gem bundle twice in a row, it will do nothing the
|
174
|
+
second time, since the gems obviously resolve against the installed gems,
|
175
|
+
and they are all expanded.
|
176
|
+
|
177
|
+
This also means that if you run `gem bundle`, and .gitignore the expanded
|
178
|
+
copies, leaving only the cached `.gem` files, you can run `gem bundle` again
|
179
|
+
on the remote system, and it will only expand out the gems (but not
|
180
|
+
resolve or download `.gem` files). This also means that native gems
|
181
|
+
will be compiled for the target platform without requiring that the
|
182
|
+
`.gem` file itself be downloaded from a remote gem server.
|
183
|
+
|
184
|
+
Assuming a Rails app with Bundler's standard setup, add something like
|
185
|
+
this to your top-level `.gitignore` to only keep the cache:
|
186
|
+
|
187
|
+
bin/*
|
188
|
+
vendor/gems/*
|
189
|
+
!vendor/gems/cache/
|
190
|
+
|
191
|
+
Make sure that you explicitly `git add vendor/gems/cache` before you commit.
|
192
|
+
|
193
|
+
### Gems with compile-time options
|
194
|
+
|
195
|
+
Some gems require you to pass compile-time options to the gem install command.
|
196
|
+
For instance, to install mysql, you might do:
|
197
|
+
|
198
|
+
gem install mysql -- --with-mysql-config=/usr/local/lib/mysql
|
199
|
+
|
200
|
+
You can pass these options to the bundler by creating a YAML file containing
|
201
|
+
the options in question:
|
202
|
+
|
203
|
+
mysql:
|
204
|
+
mysql-config: /usr/local/lib/mysql
|
205
|
+
|
206
|
+
You can then point the bundler at the file:
|
207
|
+
|
208
|
+
gem bundle --build-options build_options.yml
|
209
|
+
|
210
|
+
In general, you will want to keep the build options YAML out of version control,
|
211
|
+
and provide the appropriate options for the system in question.
|
212
|
+
|
213
|
+
### Running your application
|
214
|
+
|
215
|
+
The easiest way to run your application is to start it with an executable
|
216
|
+
copied to the specified bin directory (by default, simply bin). For example,
|
217
|
+
if the application in question is a rack app, start it with `bin/rackup`.
|
218
|
+
This will automatically set the gem environment correctly.
|
219
|
+
|
220
|
+
Another way to run arbitrary ruby code in context of the bundled gems is to
|
221
|
+
run it with the `gem exec` command. For example:
|
222
|
+
|
223
|
+
gem exec ruby my_ruby_script.rb
|
224
|
+
|
225
|
+
You can use `gem exec bash` to enter a shell that will run all binaries in
|
226
|
+
the current context.
|
227
|
+
|
228
|
+
Yet another way is to manually require the environment file first. This is
|
229
|
+
located in `[bundle_path]/gems/environment.rb`. For example:
|
230
|
+
|
231
|
+
ruby -r vendor/gems/environment.rb my_ruby_script.rb
|
232
|
+
|
233
|
+
### Using Bundler with Rails today
|
234
|
+
|
235
|
+
It should be possible to use Bundler with Rails today. Here are the steps
|
236
|
+
to follow.
|
237
|
+
|
238
|
+
* In your rails app, create a Gemfile and specify the gems that your
|
239
|
+
application depends on. Make sure to specify rails as well:
|
240
|
+
|
241
|
+
gem "rails", "2.1.2"
|
242
|
+
gem "will_paginate"
|
243
|
+
|
244
|
+
# Optionally, you can disable system gems all together and only
|
245
|
+
# use bundled gems.
|
246
|
+
disable_system_gems
|
247
|
+
|
248
|
+
* Run `gem bundle`
|
249
|
+
|
250
|
+
* You can now use rails if you prepend `gem exec` to every call to `script/*`
|
251
|
+
but that isn't fun.
|
252
|
+
|
253
|
+
* At the top of `config/preinitializer.rb`, add the following line:
|
254
|
+
|
255
|
+
require "#{RAILS_ROOT}/vendor/gems/environment"
|
256
|
+
|
257
|
+
In theory, this should be enough to get going.
|
258
|
+
|
259
|
+
## To require rubygems or not
|
260
|
+
|
261
|
+
Ideally, no gem would assume the presence of rubygems at runtime. Rubygems provides
|
262
|
+
enough features so that this isn't necessary. However, there are a number of gems
|
263
|
+
that require specific rubygems features.
|
264
|
+
|
265
|
+
If the `disable_rubygems` option is used, Bundler will stub out the most common
|
266
|
+
of these features, but it is possible that things will not go as intended quite
|
267
|
+
yet. So, if you are brave, try your code without rubygems at runtime.
|
268
|
+
|
269
|
+
This is different from the `disable_system_gems` option, which uses the rubygems
|
270
|
+
library, but prevents system gems from being loaded; only gems that are bundled
|
271
|
+
will be available to your application. This option guarantees that dependencies
|
272
|
+
of your application will be available to a remote system.
|
273
|
+
|
274
|
+
## Known Issues
|
275
|
+
|
276
|
+
* When a gem points to a git repository, the git repository will be cloned
|
277
|
+
every time Bundler does a gem dependency resolve.
|
278
|
+
|
279
|
+
## Reporting bugs
|
280
|
+
|
281
|
+
Please report all bugs on the github issue tracker for the project located
|
282
|
+
at:
|
283
|
+
|
284
|
+
http://github.com/wycats/bundler/issues/
|
data/Rakefile
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), 'lib')
|
2
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
3
|
+
require 'rubygems/specification'
|
4
|
+
require 'bundler08'
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
spec = Gem::Specification.new do |s|
|
8
|
+
s.name = "bundler08"
|
9
|
+
s.version = Bundler::VERSION
|
10
|
+
s.authors = ["Yehuda Katz", "Carl Lerche"]
|
11
|
+
s.email = ["wycats@gmail.com", "clerche@engineyard.com"]
|
12
|
+
s.homepage = "http://github.com/wycats/bundler"
|
13
|
+
s.description = s.summary = "An easy way to vendor gem dependencies"
|
14
|
+
|
15
|
+
s.platform = Gem::Platform::RUBY
|
16
|
+
s.has_rdoc = true
|
17
|
+
s.extra_rdoc_files = ["README.markdown", "LICENSE"]
|
18
|
+
|
19
|
+
s.required_rubygems_version = ">= 1.3.5"
|
20
|
+
|
21
|
+
s.require_path = 'lib'
|
22
|
+
s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("lib/**/*")
|
23
|
+
end
|
24
|
+
|
25
|
+
task :default => :spec
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'spec/rake/spectask'
|
29
|
+
rescue LoadError
|
30
|
+
task(:spec) { $stderr.puts '`gem install rspec` to run specs' }
|
31
|
+
else
|
32
|
+
desc "Run specs"
|
33
|
+
Spec::Rake::SpecTask.new do |t|
|
34
|
+
t.spec_files = FileList['spec/**/*_spec.rb'] - FileList['spec/fixtures/**/*_spec.rb']
|
35
|
+
t.spec_opts = %w(-fs --color)
|
36
|
+
t.warning = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
namespace :spec do
|
41
|
+
file "tmp/rg_deps" do
|
42
|
+
repo = File.dirname(__FILE__) + '/tmp/rg_deps'
|
43
|
+
FileUtils.mkdir_p(repo)
|
44
|
+
p repo
|
45
|
+
ENV['GEM_HOME'], ENV['GEM_PATH'] = repo, repo
|
46
|
+
system "gem install builder --no-rdoc --no-ri"
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Do all setup needed to run the specs"
|
50
|
+
task :setup => "tmp/rg_deps"
|
51
|
+
|
52
|
+
desc "Mount a ramdisk at ./tmp for faster specs"
|
53
|
+
task :ramdisk do
|
54
|
+
sh 'diskutil erasevolume HFS+ "tmpbundler" `hdiutil attach -nomount ram://116543`'
|
55
|
+
File.symlink "/Volumes/tmpbundler", File.expand_path('../tmp', __FILE__)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
spec_file = "#{spec.name}.gemspec"
|
60
|
+
desc "Create #{spec_file}"
|
61
|
+
file spec_file => "Rakefile" do
|
62
|
+
File.open(spec_file, "w") do |file|
|
63
|
+
file.puts spec.to_ruby
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
begin
|
68
|
+
require 'rake/gempackagetask'
|
69
|
+
rescue LoadError
|
70
|
+
task(:gem) { $stderr.puts '`gem install rake` to package gems' }
|
71
|
+
else
|
72
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
73
|
+
pkg.gem_spec = spec
|
74
|
+
end
|
75
|
+
task :gem => spec_file
|
76
|
+
end
|
77
|
+
|
78
|
+
desc "install the gem locally"
|
79
|
+
task :install => :package do
|
80
|
+
sh %{gem install pkg/#{spec.name}-#{spec.version}}
|
81
|
+
end
|
data/lib/bundler08.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'logger'
|
3
|
+
require 'set'
|
4
|
+
require 'erb'
|
5
|
+
# Required elements of rubygems
|
6
|
+
require "rubygems/remote_fetcher"
|
7
|
+
require "rubygems/installer"
|
8
|
+
|
9
|
+
require "bundler08/gem_bundle"
|
10
|
+
require "bundler08/source"
|
11
|
+
require "bundler08/finder"
|
12
|
+
require "bundler08/gem_ext"
|
13
|
+
require "bundler08/resolver"
|
14
|
+
require "bundler08/environment"
|
15
|
+
require "bundler08/dsl"
|
16
|
+
require "bundler08/cli"
|
17
|
+
require "bundler08/bundle"
|
18
|
+
require "bundler08/dependency"
|
19
|
+
require "bundler08/remote_specification"
|
20
|
+
|
21
|
+
module Bundler
|
22
|
+
VERSION = "0.8.2"
|
23
|
+
|
24
|
+
class << self
|
25
|
+
attr_writer :logger, :mode
|
26
|
+
|
27
|
+
def logger
|
28
|
+
@logger ||= begin
|
29
|
+
logger = Logger.new(STDOUT, Logger::INFO)
|
30
|
+
logger.formatter = proc {|_,_,_,msg| "#{msg}\n" }
|
31
|
+
logger
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def local?
|
36
|
+
@mode == :local
|
37
|
+
end
|
38
|
+
|
39
|
+
def writable?
|
40
|
+
@mode != :readonly
|
41
|
+
end
|
42
|
+
|
43
|
+
def remote?
|
44
|
+
@mode == :readwrite
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
self.mode = :readonly
|
49
|
+
end
|
@@ -0,0 +1,314 @@
|
|
1
|
+
module Bundler
|
2
|
+
class InvalidRepository < StandardError ; end
|
3
|
+
|
4
|
+
class Bundle
|
5
|
+
attr_reader :gemfile, :environment
|
6
|
+
|
7
|
+
def self.load(gemfile = nil)
|
8
|
+
gemfile = Pathname.new(gemfile || default_gemfile).expand_path
|
9
|
+
|
10
|
+
unless gemfile.file?
|
11
|
+
raise ManifestFileNotFound, "Manifest file not found: #{gemfile.to_s.inspect}"
|
12
|
+
end
|
13
|
+
|
14
|
+
new(gemfile)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.default_gemfile
|
18
|
+
current = Pathname.new(Dir.pwd)
|
19
|
+
|
20
|
+
until current.root?
|
21
|
+
filename = current.join("Gemfile")
|
22
|
+
return filename if filename.exist?
|
23
|
+
current = current.parent
|
24
|
+
end
|
25
|
+
|
26
|
+
raise DefaultManifestNotFound
|
27
|
+
end
|
28
|
+
|
29
|
+
# TODO: passing in the filename is not good
|
30
|
+
def initialize(gemfile)
|
31
|
+
@gemfile = gemfile
|
32
|
+
@environment = Environment.new(self)
|
33
|
+
Dsl.evaluate(gemfile, self, @environment)
|
34
|
+
|
35
|
+
# path = env.gem_path
|
36
|
+
|
37
|
+
FileUtils.mkdir_p(gem_path)
|
38
|
+
|
39
|
+
@cache_path = gem_path.join('cache')
|
40
|
+
@cache = GemDirectorySource.new(self, :location => @cache_path)
|
41
|
+
|
42
|
+
@specs_path = gem_path.join('specifications')
|
43
|
+
@gems_path = gem_path.join('gems')
|
44
|
+
end
|
45
|
+
|
46
|
+
def root
|
47
|
+
gemfile.parent
|
48
|
+
end
|
49
|
+
|
50
|
+
def path
|
51
|
+
@path ||= root.join("vendor/gems")
|
52
|
+
end
|
53
|
+
|
54
|
+
def path=(path)
|
55
|
+
@path = (path.relative? ? root.join(path) : path).expand_path
|
56
|
+
end
|
57
|
+
|
58
|
+
def gem_path
|
59
|
+
path.join("#{Gem.ruby_engine}/#{Gem::ConfigMap[:ruby_version]}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def bindir
|
63
|
+
@bindir ||= root.join("bin")
|
64
|
+
end
|
65
|
+
|
66
|
+
def bindir=(path)
|
67
|
+
@bindir = (path.relative? ? root.join(path) : path).expand_path
|
68
|
+
end
|
69
|
+
|
70
|
+
def install(options = {})
|
71
|
+
dependencies = @environment.dependencies
|
72
|
+
sources = @environment.sources
|
73
|
+
|
74
|
+
# ========== from env
|
75
|
+
if only_envs = options[:only]
|
76
|
+
dependencies.reject! { |d| !only_envs.any? {|env| d.in?(env) } }
|
77
|
+
end
|
78
|
+
# ==========
|
79
|
+
|
80
|
+
# Check to see whether the existing cache meets all the requirements
|
81
|
+
begin
|
82
|
+
valid = nil
|
83
|
+
# valid = Resolver.resolve(dependencies, [source_index], source_requirements)
|
84
|
+
rescue Bundler::GemNotFound
|
85
|
+
end
|
86
|
+
|
87
|
+
sources = only_local(sources) if options[:cached]
|
88
|
+
|
89
|
+
# Check the remote sources if the existing cache does not meet the requirements
|
90
|
+
# or the user passed --update
|
91
|
+
if options[:update] || !valid
|
92
|
+
Bundler.logger.info "Calculating dependencies..."
|
93
|
+
bundle = Resolver.resolve(dependencies, [@cache] + sources)
|
94
|
+
download(bundle, options)
|
95
|
+
do_install(bundle, options)
|
96
|
+
valid = bundle
|
97
|
+
end
|
98
|
+
|
99
|
+
generate_bins(valid, options)
|
100
|
+
cleanup(valid, options)
|
101
|
+
configure(valid, options)
|
102
|
+
|
103
|
+
Bundler.logger.info "Done."
|
104
|
+
end
|
105
|
+
|
106
|
+
def cache(*gemfiles)
|
107
|
+
FileUtils.mkdir_p(@cache_path)
|
108
|
+
gemfiles.each do |gemfile|
|
109
|
+
Bundler.logger.info "Caching: #{File.basename(gemfile)}"
|
110
|
+
FileUtils.cp(gemfile, @cache_path)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def list_outdated(options={})
|
115
|
+
outdated_gems = source_index.outdated.sort
|
116
|
+
|
117
|
+
if outdated_gems.empty?
|
118
|
+
Bundler.logger.info "All gems are up to date."
|
119
|
+
else
|
120
|
+
Bundler.logger.info "Outdated gems:"
|
121
|
+
outdated_gems.each do |name|
|
122
|
+
Bundler.logger.info " * #{name}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def prune(options = {})
|
128
|
+
dependencies, sources = @environment.dependencies, @environment.sources
|
129
|
+
|
130
|
+
sources = only_local(sources)
|
131
|
+
bundle = Resolver.resolve(dependencies, [@cache] + sources)
|
132
|
+
@cache.gems.each do |name, specs|
|
133
|
+
specs.each do |spec|
|
134
|
+
unless bundle.any? { |s| s.name == spec.name && s.version == spec.version }
|
135
|
+
Bundler.logger.info "Pruning #{spec.name} (#{spec.version}) from the cache"
|
136
|
+
FileUtils.rm @cache_path.join("#{spec.full_name}.gem")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def list(options = {})
|
143
|
+
Bundler.logger.info "Currently bundled gems:"
|
144
|
+
gems.each do |spec|
|
145
|
+
Bundler.logger.info " * #{spec.name} (#{spec.version})"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def gems
|
150
|
+
source_index.gems.values
|
151
|
+
end
|
152
|
+
|
153
|
+
def source_index
|
154
|
+
index = Gem::SourceIndex.from_gems_in(@specs_path)
|
155
|
+
index.each { |n, spec| spec.loaded_from = @specs_path.join("#{spec.full_name}.gemspec") }
|
156
|
+
index
|
157
|
+
end
|
158
|
+
|
159
|
+
def download_path_for(type)
|
160
|
+
@repos[type].download_path_for
|
161
|
+
end
|
162
|
+
|
163
|
+
def setup_environment
|
164
|
+
unless @environment.system_gems
|
165
|
+
ENV["GEM_HOME"] = gem_path
|
166
|
+
ENV["GEM_PATH"] = gem_path
|
167
|
+
end
|
168
|
+
ENV["PATH"] = "#{bindir}:#{ENV["PATH"]}"
|
169
|
+
ENV["RUBYOPT"] = "-r#{gem_path}/environment #{ENV["RUBYOPT"]}"
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
def only_local(sources)
|
175
|
+
sources.select { |s| s.local? }
|
176
|
+
end
|
177
|
+
|
178
|
+
def download(bundle, options)
|
179
|
+
bundle.sort_by {|s| s.full_name.downcase }.each do |spec|
|
180
|
+
next if spec.no_bundle?
|
181
|
+
spec.source.download(spec)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def do_install(bundle, options)
|
186
|
+
bundle.each do |spec|
|
187
|
+
next if spec.no_bundle?
|
188
|
+
spec.loaded_from = @specs_path.join("#{spec.full_name}.gemspec")
|
189
|
+
# Do nothing if the gem is already expanded
|
190
|
+
next if @gems_path.join(spec.full_name).directory?
|
191
|
+
|
192
|
+
case spec.source
|
193
|
+
when GemSource, GemDirectorySource, SystemGemSource
|
194
|
+
expand_gemfile(spec, options)
|
195
|
+
else
|
196
|
+
expand_vendored_gem(spec, options)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def generate_bins(bundle, options)
|
202
|
+
bundle.each do |spec|
|
203
|
+
next if spec.no_bundle?
|
204
|
+
# HAX -- Generate the bin
|
205
|
+
bin_dir = bindir
|
206
|
+
path = gem_path
|
207
|
+
gems_path = @gems_path
|
208
|
+
installer = Gem::Installer.allocate
|
209
|
+
installer.instance_eval do
|
210
|
+
@spec = spec
|
211
|
+
@bin_dir = bin_dir
|
212
|
+
@gem_dir = gems_path.join(spec.full_name)
|
213
|
+
@gem_home = path
|
214
|
+
@wrappers = true
|
215
|
+
@format_executable = false
|
216
|
+
@env_shebang = false
|
217
|
+
end
|
218
|
+
installer.generate_bin
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def expand_gemfile(spec, options)
|
223
|
+
Bundler.logger.info "Installing #{spec.name} (#{spec.version})"
|
224
|
+
|
225
|
+
gemfile = @cache_path.join("#{spec.full_name}.gem").to_s
|
226
|
+
|
227
|
+
if build_args = options[:build_options] && options[:build_options][spec.name]
|
228
|
+
Gem::Command.build_args = build_args.map {|k,v| "--with-#{k}=#{v}"}
|
229
|
+
end
|
230
|
+
|
231
|
+
installer = Gem::Installer.new(gemfile, options.merge(
|
232
|
+
:install_dir => gem_path,
|
233
|
+
:ignore_dependencies => true,
|
234
|
+
:env_shebang => true,
|
235
|
+
:wrappers => true,
|
236
|
+
:bin_dir => bindir
|
237
|
+
))
|
238
|
+
installer.install
|
239
|
+
rescue Gem::InstallError
|
240
|
+
cleanup_spec(spec)
|
241
|
+
raise
|
242
|
+
ensure
|
243
|
+
Gem::Command.build_args = []
|
244
|
+
end
|
245
|
+
|
246
|
+
def expand_vendored_gem(spec, options)
|
247
|
+
add_spec(spec)
|
248
|
+
FileUtils.mkdir_p(@gems_path)
|
249
|
+
File.symlink(spec.location, @gems_path.join(spec.full_name))
|
250
|
+
end
|
251
|
+
|
252
|
+
def add_spec(spec)
|
253
|
+
destination = @specs_path
|
254
|
+
destination.mkdir unless destination.exist?
|
255
|
+
|
256
|
+
File.open(destination.join("#{spec.full_name}.gemspec"), 'w') do |f|
|
257
|
+
f.puts spec.to_ruby
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def cleanup(valid, options)
|
262
|
+
to_delete = gems
|
263
|
+
to_delete.delete_if do |spec|
|
264
|
+
valid.any? { |other| spec.name == other.name && spec.version == other.version }
|
265
|
+
end
|
266
|
+
|
267
|
+
valid_executables = valid.map { |s| s.executables }.flatten.compact
|
268
|
+
|
269
|
+
to_delete.each do |spec|
|
270
|
+
Bundler.logger.info "Deleting gem: #{spec.name} (#{spec.version})"
|
271
|
+
cleanup_spec(spec)
|
272
|
+
# Cleanup the bin directory
|
273
|
+
spec.executables.each do |bin|
|
274
|
+
next if valid_executables.include?(bin)
|
275
|
+
Bundler.logger.info "Deleting bin file: #{bin}"
|
276
|
+
FileUtils.rm_rf(bindir.join(bin))
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def cleanup_spec(spec)
|
282
|
+
FileUtils.rm_rf(@specs_path.join("#{spec.full_name}.gemspec"))
|
283
|
+
FileUtils.rm_rf(@gems_path.join(spec.full_name))
|
284
|
+
end
|
285
|
+
|
286
|
+
def expand(options)
|
287
|
+
each_repo do |repo|
|
288
|
+
repo.expand(options)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def configure(specs, options)
|
293
|
+
FileUtils.mkdir_p(gem_path)
|
294
|
+
|
295
|
+
File.open(gem_path.join("environment.rb"), "w") do |file|
|
296
|
+
file.puts @environment.environment_rb(specs, options)
|
297
|
+
end
|
298
|
+
|
299
|
+
generate_environment_picker
|
300
|
+
end
|
301
|
+
|
302
|
+
def generate_environment_picker
|
303
|
+
FileUtils.cp("#{File.dirname(__FILE__)}/templates/environment_picker.erb", path.join("environment.rb"))
|
304
|
+
end
|
305
|
+
|
306
|
+
def require_code(file, dep)
|
307
|
+
constraint = case
|
308
|
+
when dep.only then %{ if #{dep.only.inspect}.include?(env)}
|
309
|
+
when dep.except then %{ unless #{dep.except.inspect}.include?(env)}
|
310
|
+
end
|
311
|
+
"require #{file.inspect}#{constraint}"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|