2007-11-10 02:48:56 -05:00
|
|
|
require 'rubygems/command'
|
|
|
|
|
|
|
|
class Gem::Commands::LockCommand < Gem::Command
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
super 'lock', 'Generate a lockdown list of gems',
|
|
|
|
:strict => false
|
|
|
|
|
|
|
|
add_option '-s', '--[no-]strict',
|
|
|
|
'fail if unable to satisfy a dependency' do |strict, options|
|
|
|
|
options[:strict] = strict
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def arguments # :nodoc:
|
|
|
|
"GEMNAME name of gem to lock\nVERSION version of gem to lock"
|
|
|
|
end
|
|
|
|
|
|
|
|
def defaults_str # :nodoc:
|
|
|
|
"--no-strict"
|
|
|
|
end
|
|
|
|
|
|
|
|
def description # :nodoc:
|
|
|
|
<<-EOF
|
|
|
|
The lock command will generate a list of +gem+ statements that will lock down
|
|
|
|
the versions for the gem given in the command line. It will specify exact
|
|
|
|
versions in the requirements list to ensure that the gems loaded will always
|
|
|
|
be consistent. A full recursive search of all effected gems will be
|
|
|
|
generated.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
gemlock rails-1.0.0 > lockdown.rb
|
|
|
|
|
|
|
|
will produce in lockdown.rb:
|
|
|
|
|
|
|
|
require "rubygems"
|
|
|
|
gem 'rails', '= 1.0.0'
|
|
|
|
gem 'rake', '= 0.7.0.1'
|
|
|
|
gem 'activesupport', '= 1.2.5'
|
|
|
|
gem 'activerecord', '= 1.13.2'
|
|
|
|
gem 'actionpack', '= 1.11.2'
|
|
|
|
gem 'actionmailer', '= 1.1.5'
|
|
|
|
gem 'actionwebservice', '= 1.0.0'
|
|
|
|
|
|
|
|
Just load lockdown.rb from your application to ensure that the current
|
|
|
|
versions are loaded. Make sure that lockdown.rb is loaded *before* any
|
|
|
|
other require statements.
|
|
|
|
|
|
|
|
Notice that rails 1.0.0 only requires that rake 0.6.2 or better be used.
|
|
|
|
Rake-0.7.0.1 is the most recent version installed that satisfies that, so we
|
|
|
|
lock it down to the exact version.
|
|
|
|
EOF
|
|
|
|
end
|
|
|
|
|
|
|
|
def usage # :nodoc:
|
|
|
|
"#{program_name} GEMNAME-VERSION [GEMNAME-VERSION ...]"
|
|
|
|
end
|
|
|
|
|
|
|
|
def complain(message)
|
2008-09-25 06:13:50 -04:00
|
|
|
if options[:strict] then
|
|
|
|
raise Gem::Exception, message
|
2007-11-10 02:48:56 -05:00
|
|
|
else
|
|
|
|
say "# #{message}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def execute
|
2008-09-25 06:13:50 -04:00
|
|
|
say "require 'rubygems'"
|
2007-11-10 02:48:56 -05:00
|
|
|
|
|
|
|
locked = {}
|
|
|
|
|
|
|
|
pending = options[:args]
|
|
|
|
|
|
|
|
until pending.empty? do
|
|
|
|
full_name = pending.shift
|
|
|
|
|
|
|
|
spec = Gem::SourceIndex.load_specification spec_path(full_name)
|
|
|
|
|
2008-09-25 06:13:50 -04:00
|
|
|
if spec.nil? then
|
|
|
|
complain "Could not find gem #{full_name}, try using the full name"
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name]
|
|
|
|
locked[spec.name] = true
|
|
|
|
|
2008-06-17 18:04:18 -04:00
|
|
|
spec.runtime_dependencies.each do |dep|
|
2007-11-10 02:48:56 -05:00
|
|
|
next if locked[dep.name]
|
2008-09-25 06:13:50 -04:00
|
|
|
candidates = Gem.source_index.search dep
|
2007-11-10 02:48:56 -05:00
|
|
|
|
|
|
|
if candidates.empty? then
|
2008-09-25 06:13:50 -04:00
|
|
|
complain "Unable to satisfy '#{dep}' from currently installed gems"
|
2007-11-10 02:48:56 -05:00
|
|
|
else
|
|
|
|
pending << candidates.last.full_name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def spec_path(gem_full_name)
|
2008-09-25 06:13:50 -04:00
|
|
|
gemspecs = Gem.path.map do |path|
|
|
|
|
File.join path, "specifications", "#{gem_full_name}.gemspec"
|
|
|
|
end
|
|
|
|
|
|
|
|
gemspecs.find { |gemspec| File.exist? gemspec }
|
2007-11-10 02:48:56 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|