1require 'rubygems/command' 2 3class Gem::Commands::LockCommand < Gem::Command 4 5 def initialize 6 super 'lock', 'Generate a lockdown list of gems', 7 :strict => false 8 9 add_option '-s', '--[no-]strict', 10 'fail if unable to satisfy a dependency' do |strict, options| 11 options[:strict] = strict 12 end 13 end 14 15 def arguments # :nodoc: 16 "GEMNAME name of gem to lock\nVERSION version of gem to lock" 17 end 18 19 def defaults_str # :nodoc: 20 "--no-strict" 21 end 22 23 def description # :nodoc: 24 <<-EOF 25The lock command will generate a list of +gem+ statements that will lock down 26the versions for the gem given in the command line. It will specify exact 27versions in the requirements list to ensure that the gems loaded will always 28be consistent. A full recursive search of all effected gems will be 29generated. 30 31Example: 32 33 gem lock rails-1.0.0 > lockdown.rb 34 35will produce in lockdown.rb: 36 37 require "rubygems" 38 gem 'rails', '= 1.0.0' 39 gem 'rake', '= 0.7.0.1' 40 gem 'activesupport', '= 1.2.5' 41 gem 'activerecord', '= 1.13.2' 42 gem 'actionpack', '= 1.11.2' 43 gem 'actionmailer', '= 1.1.5' 44 gem 'actionwebservice', '= 1.0.0' 45 46Just load lockdown.rb from your application to ensure that the current 47versions are loaded. Make sure that lockdown.rb is loaded *before* any 48other require statements. 49 50Notice that rails 1.0.0 only requires that rake 0.6.2 or better be used. 51Rake-0.7.0.1 is the most recent version installed that satisfies that, so we 52lock it down to the exact version. 53 EOF 54 end 55 56 def usage # :nodoc: 57 "#{program_name} GEMNAME-VERSION [GEMNAME-VERSION ...]" 58 end 59 60 def complain(message) 61 if options[:strict] then 62 raise Gem::Exception, message 63 else 64 say "# #{message}" 65 end 66 end 67 68 def execute 69 say "require 'rubygems'" 70 71 locked = {} 72 73 pending = options[:args] 74 75 until pending.empty? do 76 full_name = pending.shift 77 78 spec = Gem::Specification.load spec_path(full_name) 79 80 if spec.nil? then 81 complain "Could not find gem #{full_name}, try using the full name" 82 next 83 end 84 85 say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name] 86 locked[spec.name] = true 87 88 spec.runtime_dependencies.each do |dep| 89 next if locked[dep.name] 90 candidates = dep.matching_specs 91 92 if candidates.empty? then 93 complain "Unable to satisfy '#{dep}' from currently installed gems" 94 else 95 pending << candidates.last.full_name 96 end 97 end 98 end 99 end 100 101 def spec_path(gem_full_name) 102 gemspecs = Gem.path.map { |path| 103 File.join path, "specifications", "#{gem_full_name}.gemspec" 104 } 105 106 gemspecs.find { |path| File.exist? path } 107 end 108 109end 110 111