boxgrinder-build 0.3.6 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/boxgrinder-build +4 -4
- data/lib/boxgrinder-build/helpers/guestfs-helper.rb +26 -2
- data/lib/boxgrinder-build/plugins/delivery/s3/aws-helper.rb +6 -10
- data/lib/boxgrinder-build/plugins/delivery/s3/s3-plugin.rb +25 -7
- data/lib/boxgrinder-build/plugins/os/base/rpm-based-os-plugin.rb +1 -21
- data/lib/boxgrinder-build/plugins/os/base/validators/rpm-dependency-validator.rb +0 -1
- data/lib/boxgrinder-build/plugins/os/fedora/fedora-plugin.rb +9 -1
- data/lib/boxgrinder-build/plugins/platform/ec2/ec2-plugin.rb +59 -23
- metadata +5 -5
data/bin/boxgrinder-build
CHANGED
@@ -26,7 +26,7 @@ require 'boxgrinder-core/helpers/log-helper'
|
|
26
26
|
require 'boxgrinder-build/helpers/plugin-helper'
|
27
27
|
require 'boxgrinder-build/appliance'
|
28
28
|
|
29
|
-
gem 'boxgrinder-core', '>= 0.0.
|
29
|
+
gem 'boxgrinder-core', '>= 0.0.10'
|
30
30
|
gem 'aws-s3', '>= 0.6.2'
|
31
31
|
gem 'amazon-ec2', '>= 0.9.6'
|
32
32
|
gem 'net-sftp', '>= 2.0.4'
|
@@ -39,8 +39,8 @@ gem 'commander', '>= 4.0.3'
|
|
39
39
|
|
40
40
|
#$stderr.reopen('/dev/null')
|
41
41
|
|
42
|
-
program :name, 'BoxGrinder'
|
43
|
-
program :version, '0.3.
|
42
|
+
program :name, 'BoxGrinder Build'
|
43
|
+
program :version, '0.3.7'
|
44
44
|
program :description, 'A tool for building VM images from simple definition files.'
|
45
45
|
default_command :build
|
46
46
|
|
@@ -75,7 +75,7 @@ module BoxGrinder
|
|
75
75
|
|
76
76
|
appliance_definition_file = args.shift or raise('Appliance definition file is required. Run boxgrinder-build -h for more info')
|
77
77
|
|
78
|
-
raise
|
78
|
+
raise "Appliance definition file '#{appliance_definition_file}' could not be found" unless File.exists?( appliance_definition_file )
|
79
79
|
raise "Not known platform: #{options.platform}. Available platforms: #{PlatformPluginManager.instance.plugins.keys.join(', ')}." if PlatformPluginManager.instance.plugins[options.platform].nil? and options.platform != :none
|
80
80
|
raise "Not known delivery type: #{options.delivery}. Available types: #{DeliveryPluginManager.instance.types.keys.join(', ')}." if DeliveryPluginManager.instance.types.keys.include?(options.delivery).nil? and options.delivery != :none
|
81
81
|
|
@@ -56,8 +56,14 @@ module BoxGrinder
|
|
56
56
|
@log.debug "Launching guestfs..."
|
57
57
|
@guestfs.launch
|
58
58
|
|
59
|
-
|
60
|
-
|
59
|
+
case @guestfs.list_partitions.size
|
60
|
+
when 0
|
61
|
+
mount_partition( @guestfs.list_devices.first, '/' )
|
62
|
+
when 1
|
63
|
+
mount_partition( @guestfs.list_partitions.first, '/' )
|
64
|
+
else
|
65
|
+
mount_partitions
|
66
|
+
end
|
61
67
|
|
62
68
|
@log.debug "Guestfs launched."
|
63
69
|
end
|
@@ -85,5 +91,23 @@ module BoxGrinder
|
|
85
91
|
@guestfs.sh( "rpm --rebuilddb" )
|
86
92
|
@log.debug "Cleaning RPM database finished."
|
87
93
|
end
|
94
|
+
|
95
|
+
def mount_partitions
|
96
|
+
root_partition = nil
|
97
|
+
|
98
|
+
@guestfs.list_partitions.each do |partition|
|
99
|
+
mount_partition( partition, '/' )
|
100
|
+
if @guestfs.exists( '/sbin/e2label' ) != 0
|
101
|
+
root_partition = partition
|
102
|
+
break
|
103
|
+
end
|
104
|
+
@guestfs.umount( partition )
|
105
|
+
end
|
106
|
+
|
107
|
+
@guestfs.list_partitions.each do |partition|
|
108
|
+
next if partition == root_partition
|
109
|
+
mount_partition( partition, @guestfs.sh( "/sbin/e2label #{partition}" ).chomp.strip )
|
110
|
+
end
|
111
|
+
end
|
88
112
|
end
|
89
113
|
end
|
@@ -39,19 +39,15 @@ module BoxGrinder
|
|
39
39
|
attr_reader :ec2
|
40
40
|
attr_reader :s3
|
41
41
|
|
42
|
-
def bucket_key( appliance_name )
|
43
|
-
"#{@plugin_config['bucket']}/#{appliance_name}/#{@appliance_config.version}.#{@appliance_config.release}/#{@appliance_config.hardware.arch}"
|
42
|
+
def bucket_key( appliance_name, path )
|
43
|
+
"#{@plugin_config['bucket']}#{path}/#{appliance_name}/#{@appliance_config.version}.#{@appliance_config.release}/#{@appliance_config.hardware.arch}"
|
44
44
|
end
|
45
45
|
|
46
|
-
def bucket_manifest_key( appliance_name )
|
47
|
-
"#{bucket_key( appliance_name )}/#{appliance_name}.ec2.manifest.xml"
|
46
|
+
def bucket_manifest_key( appliance_name, path )
|
47
|
+
"#{bucket_key( appliance_name, path )}/#{appliance_name}.ec2.manifest.xml"
|
48
48
|
end
|
49
49
|
|
50
|
-
def
|
51
|
-
!ami_info( appliance_name ).nil?
|
52
|
-
end
|
53
|
-
|
54
|
-
def ami_info( appliance_name )
|
50
|
+
def ami_info( appliance_name, path )
|
55
51
|
ami_info = nil
|
56
52
|
|
57
53
|
images = @ec2.describe_images( :owner_id => @plugin_config['account_number'] ).imagesSet
|
@@ -59,7 +55,7 @@ module BoxGrinder
|
|
59
55
|
return nil if images.nil?
|
60
56
|
|
61
57
|
for image in images.item do
|
62
|
-
ami_info = image if (image.imageLocation.eql?( bucket_manifest_key( appliance_name ) ))
|
58
|
+
ami_info = image if (image.imageLocation.eql?( bucket_manifest_key( appliance_name, path ) ))
|
63
59
|
end
|
64
60
|
|
65
61
|
ami_info
|
@@ -28,7 +28,9 @@ module BoxGrinder
|
|
28
28
|
class S3Plugin < BaseDeliveryPlugin
|
29
29
|
|
30
30
|
AMI_OSES = {
|
31
|
-
'fedora'
|
31
|
+
'fedora' => [ '11' ],
|
32
|
+
'centos' => [ '5' ],
|
33
|
+
'rhel' => [ '5' ]
|
32
34
|
}
|
33
35
|
|
34
36
|
KERNELS = {
|
@@ -38,6 +40,23 @@ module BoxGrinder
|
|
38
40
|
'i386' => { :aki => 'aki-a71cf9ce', :ari => 'ari-a51cf9cc' },
|
39
41
|
'x86_64' => { :aki => 'aki-b51cf9dc', :ari => 'ari-b31cf9da' }
|
40
42
|
}
|
43
|
+
},
|
44
|
+
'centos' => {
|
45
|
+
'5' => {
|
46
|
+
'i386' => { :aki => 'aki-a71cf9ce', :ari => 'ari-a51cf9cc' },
|
47
|
+
'x86_64' => { :aki => 'aki-b51cf9dc', :ari => 'ari-b31cf9da' }
|
48
|
+
}
|
49
|
+
},
|
50
|
+
'rhel' => {
|
51
|
+
'5' => {
|
52
|
+
'i386' => { :aki => 'aki-a71cf9ce', :ari => 'ari-a51cf9cc' },
|
53
|
+
'x86_64' => { :aki => 'aki-b51cf9dc', :ari => 'ari-b31cf9da' }
|
54
|
+
}
|
55
|
+
# '5' => {
|
56
|
+
# 'i386' => { :aki => 'aki-e3a54b8a', :ari => 'ari-f9a54b90' },
|
57
|
+
# 'x86_64' => { :aki => 'aki-ffa54b96', :ari => 'ari-fda54b94' }
|
58
|
+
# }
|
59
|
+
|
41
60
|
}
|
42
61
|
}
|
43
62
|
}
|
@@ -135,12 +154,12 @@ module BoxGrinder
|
|
135
154
|
|
136
155
|
def image_already_uploaded?
|
137
156
|
begin
|
138
|
-
bucket = Bucket.find(@
|
157
|
+
bucket = Bucket.find(@plugin_config['bucket'])
|
139
158
|
rescue
|
140
159
|
return false
|
141
160
|
end
|
142
161
|
|
143
|
-
manifest_location = @aws_helper.bucket_manifest_key(@appliance_config.name)
|
162
|
+
manifest_location = @aws_helper.bucket_manifest_key(@appliance_config.name, @plugin_config['path'])
|
144
163
|
manifest_location = manifest_location[manifest_location.index("/") + 1, manifest_location.length]
|
145
164
|
|
146
165
|
for object in bucket.objects do
|
@@ -153,20 +172,19 @@ module BoxGrinder
|
|
153
172
|
def upload_image
|
154
173
|
@log.info "Uploading #{@appliance_config.name} AMI to bucket '#{@plugin_config['bucket']}'..."
|
155
174
|
|
156
|
-
@exec_helper.execute("ec2-upload-bundle -b #{@aws_helper.bucket_key(@appliance_config.name)} -m #{@ami_manifest} -a #{@plugin_config['access_key']} -s #{@plugin_config['secret_access_key']} --retry")
|
175
|
+
@exec_helper.execute("ec2-upload-bundle -b #{@aws_helper.bucket_key(@appliance_config.name, @plugin_config['path'])} -m #{@ami_manifest} -a #{@plugin_config['access_key']} -s #{@plugin_config['secret_access_key']} --retry")
|
157
176
|
end
|
158
177
|
|
159
178
|
def register_image
|
160
|
-
ami_info = @aws_helper.ami_info(@appliance_config.name)
|
179
|
+
ami_info = @aws_helper.ami_info(@appliance_config.name, @plugin_config['path'])
|
161
180
|
|
162
181
|
if ami_info
|
163
182
|
@log.info "Image is registered under id: #{ami_info.imageId}"
|
164
183
|
return
|
165
184
|
else
|
166
|
-
ami_info = @aws_helper.ec2.register_image(:image_location => @aws_helper.bucket_manifest_key(@appliance_config.name))
|
185
|
+
ami_info = @aws_helper.ec2.register_image(:image_location => @aws_helper.bucket_manifest_key(@appliance_config.name, @plugin_config['path']))
|
167
186
|
@log.info "Image successfully registered under id: #{ami_info.imageId}."
|
168
187
|
end
|
169
188
|
end
|
170
|
-
|
171
189
|
end
|
172
190
|
end
|
@@ -49,8 +49,6 @@ module BoxGrinder
|
|
49
49
|
customize( @deliverables[:disk] ) do |guestfs, guestfs_helper|
|
50
50
|
@log.info "Executing post operations after build..."
|
51
51
|
|
52
|
-
mount_partitions( guestfs, guestfs_helper ) if guestfs.list_partitions.size > 1
|
53
|
-
|
54
52
|
if @appliance_config.post.base.size > 0
|
55
53
|
@appliance_config.post.base.each do |cmd|
|
56
54
|
@log.debug "Executing #{cmd}"
|
@@ -74,24 +72,6 @@ module BoxGrinder
|
|
74
72
|
@log.info "Base image for #{@appliance_config.name} appliance was built successfully."
|
75
73
|
end
|
76
74
|
|
77
|
-
def mount_partitions( guestfs, guestfs_helper )
|
78
|
-
root_partition = nil
|
79
|
-
|
80
|
-
guestfs.list_partitions.each do |partition|
|
81
|
-
guestfs_helper.mount_partition( partition, '/' )
|
82
|
-
if guestfs.exists( '/sbin/e2label' ) != 0
|
83
|
-
root_partition = partition
|
84
|
-
break
|
85
|
-
end
|
86
|
-
guestfs.umount( partition )
|
87
|
-
end
|
88
|
-
|
89
|
-
guestfs.list_partitions.each do |partition|
|
90
|
-
next if partition == root_partition
|
91
|
-
guestfs_helper.mount_partition( partition, guestfs.sh( "/sbin/e2label #{partition}" ).chomp.strip )
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
75
|
def change_configuration( guestfs )
|
96
76
|
@log.debug "Changing configuration files using augeas..."
|
97
77
|
guestfs.aug_init( "/", 0 )
|
@@ -125,7 +105,7 @@ module BoxGrinder
|
|
125
105
|
@log.debug "Installing repositories from appliance definition file..."
|
126
106
|
@appliance_config.repos.each do |repo|
|
127
107
|
if repo['ephemeral']
|
128
|
-
@log.debug "Repository '#{repo['name']}' is
|
108
|
+
@log.debug "Repository '#{repo['name']}' is an ephemeral repo. It'll not be installed in the appliance."
|
129
109
|
next
|
130
110
|
end
|
131
111
|
|
@@ -72,7 +72,6 @@ module BoxGrinder
|
|
72
72
|
if invalid_names.size == 0
|
73
73
|
@log.info "All additional packages for #{@appliance_config.simple_name} appliance successfully resolved."
|
74
74
|
else
|
75
|
-
puts invalid_names.to_yaml
|
76
75
|
raise "Package#{invalid_names.size > 1 ? "s" : ""} #{invalid_names.join(', ')} for #{@appliance_config.simple_name} appliance not found in repositories. Please check package names in appliance definition file."
|
77
76
|
end
|
78
77
|
end
|
@@ -26,6 +26,14 @@ module BoxGrinder
|
|
26
26
|
class FedoraPlugin < RPMBasedOSPlugin
|
27
27
|
|
28
28
|
FEDORA_REPOS = {
|
29
|
+
"13" => {
|
30
|
+
"base" => {
|
31
|
+
"mirrorlist" => "http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-13&arch=#ARCH#"
|
32
|
+
},
|
33
|
+
"updates" => {
|
34
|
+
"mirrorlist" => "http://mirrors.fedoraproject.org/mirrorlist?repo=updates-released-f13&arch=#ARCH#"
|
35
|
+
}
|
36
|
+
},
|
29
37
|
"12" => {
|
30
38
|
"base" => {
|
31
39
|
"mirrorlist" => "http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-12&arch=#ARCH#"
|
@@ -55,7 +63,7 @@ module BoxGrinder
|
|
55
63
|
:name => :fedora,
|
56
64
|
:full_name => "Fedora",
|
57
65
|
:type => :linux,
|
58
|
-
:versions => ["11", "12", "rawhide"]
|
66
|
+
:versions => ["11", "12", "13", "rawhide"]
|
59
67
|
}
|
60
68
|
end
|
61
69
|
|
@@ -30,7 +30,9 @@ module BoxGrinder
|
|
30
30
|
end
|
31
31
|
|
32
32
|
SUPPORTED_OSES = {
|
33
|
-
'
|
33
|
+
'rhel' => [ '5' ],
|
34
|
+
'centos' => [ '5' ],
|
35
|
+
'fedora' => [ '11' ]
|
34
36
|
}
|
35
37
|
|
36
38
|
REGIONS = {'us_east' => 'url'}
|
@@ -41,8 +43,19 @@ module BoxGrinder
|
|
41
43
|
'i386' => { :rpm => 'http://repo.oddthesis.org/packages/other/kernel-xen-2.6.21.7-2.fc8.i686.rpm' },
|
42
44
|
'x86_64' => { :rpm => 'http://repo.oddthesis.org/packages/other/kernel-xen-2.6.21.7-2.fc8.x86_64.rpm' }
|
43
45
|
}
|
46
|
+
},
|
47
|
+
'centos' => {
|
48
|
+
'5' => {
|
49
|
+
'i386' => { :rpm => 'http://repo.oddthesis.org/packages/other/kernel-xen-2.6.21.7-2.fc8.i686.rpm' },
|
50
|
+
'x86_64' => { :rpm => 'http://repo.oddthesis.org/packages/other/kernel-xen-2.6.21.7-2.fc8.x86_64.rpm' }
|
51
|
+
}
|
52
|
+
},
|
53
|
+
'rhel' => {
|
54
|
+
'5' => {
|
55
|
+
'i386' => { :rpm => 'http://repo.oddthesis.org/packages/other/kernel-xen-2.6.21.7-2.fc8.i686.rpm' },
|
56
|
+
'x86_64' => { :rpm => 'http://repo.oddthesis.org/packages/other/kernel-xen-2.6.21.7-2.fc8.x86_64.rpm' }
|
57
|
+
}
|
44
58
|
}
|
45
|
-
|
46
59
|
}
|
47
60
|
|
48
61
|
def after_init
|
@@ -65,7 +78,7 @@ module BoxGrinder
|
|
65
78
|
return
|
66
79
|
end
|
67
80
|
|
68
|
-
unless SUPPORTED_OSES[@appliance_config.os.name].include?(@appliance_config.os.version)
|
81
|
+
unless !SUPPORTED_OSES[@appliance_config.os.name].nil? and SUPPORTED_OSES[@appliance_config.os.name].include?(@appliance_config.os.version)
|
69
82
|
@log.error "EC2 platform plugin for Linux operating systems supports: #{supported_os}. Your OS is #{@appliance_config.os.name} #{@appliance_config.os.version}."
|
70
83
|
return
|
71
84
|
end
|
@@ -81,23 +94,22 @@ module BoxGrinder
|
|
81
94
|
raise "Error while preparing EC2 disk image. See logs for more info"
|
82
95
|
end
|
83
96
|
|
84
|
-
raw_disk_offset = calculate_disk_offset(raw_disk)
|
85
|
-
|
86
97
|
ec2_disk_mount_dir = "#{@appliance_config.path.dir.build}/tmp/ec2-#{rand(9999999999).to_s.center(10, rand(9).to_s)}"
|
87
98
|
raw_disk_mount_dir = "#{@appliance_config.path.dir.build}/tmp/raw-#{rand(9999999999).to_s.center(10, rand(9).to_s)}"
|
88
99
|
|
89
100
|
|
90
101
|
begin
|
91
|
-
|
92
|
-
|
102
|
+
ec2_mounts = mount_image( @deliverables[:disk], ec2_disk_mount_dir )
|
103
|
+
raw_mounts = mount_image( raw_disk, raw_disk_mount_dir )
|
93
104
|
rescue => e
|
105
|
+
@log.debug e
|
94
106
|
raise "Error while mounting image. See logs for more info"
|
95
107
|
end
|
96
108
|
|
97
109
|
sync_files(raw_disk_mount_dir, ec2_disk_mount_dir)
|
98
110
|
|
99
|
-
umount_image(raw_disk, raw_disk_mount_dir,
|
100
|
-
umount_image(@deliverables[:disk], ec2_disk_mount_dir,
|
111
|
+
umount_image(raw_disk, raw_disk_mount_dir, raw_mounts)
|
112
|
+
umount_image(@deliverables[:disk], ec2_disk_mount_dir, ec2_mounts)
|
101
113
|
|
102
114
|
customize(@deliverables[:disk]) do |guestfs, guestfs_helper|
|
103
115
|
create_devices(guestfs)
|
@@ -154,30 +166,51 @@ module BoxGrinder
|
|
154
166
|
@log.debug "Filesystem created"
|
155
167
|
end
|
156
168
|
|
157
|
-
def
|
169
|
+
def calculate_disk_offsets( disk )
|
170
|
+
@log.debug "Calculating offsets for '#{File.basename(disk)}' disk..."
|
158
171
|
loop_device = get_loop_device
|
159
172
|
|
160
173
|
@exec_helper.execute("sudo losetup #{loop_device} #{disk}")
|
161
|
-
|
174
|
+
offsets = @exec_helper.execute("sudo parted #{loop_device} 'unit B print' | grep -e '^ [0-9]' | awk '{ print $2 }'").scan(/\d+/)
|
162
175
|
@exec_helper.execute("sudo losetup -d #{loop_device}")
|
163
176
|
|
164
|
-
|
177
|
+
@log.trace "Offsets:\n#{offsets}"
|
178
|
+
|
179
|
+
offsets
|
165
180
|
end
|
166
181
|
|
167
|
-
def mount_image(disk, mount_dir
|
168
|
-
|
182
|
+
def mount_image(disk, mount_dir)
|
183
|
+
offsets = calculate_disk_offsets( disk )
|
169
184
|
|
170
|
-
@log.debug "Mounting image #{File.basename(disk)} in #{mount_dir}
|
185
|
+
@log.debug "Mounting image #{File.basename(disk)} in #{mount_dir}..."
|
171
186
|
FileUtils.mkdir_p(mount_dir)
|
172
|
-
@exec_helper.execute("sudo losetup -o #{offset.to_s} #{loop_device} #{disk}")
|
173
|
-
@exec_helper.execute("sudo mount #{loop_device} -t ext3 #{ mount_dir}")
|
174
187
|
|
175
|
-
|
188
|
+
mounts = {}
|
189
|
+
|
190
|
+
offsets.each do |offset|
|
191
|
+
loop_device = get_loop_device
|
192
|
+
@exec_helper.execute("sudo losetup -o #{offset.to_s} #{loop_device} #{disk}")
|
193
|
+
label = @exec_helper.execute("e2label #{loop_device}").strip.chomp
|
194
|
+
label = '/' if label == ''
|
195
|
+
mounts[label] = loop_device
|
196
|
+
end
|
197
|
+
|
198
|
+
@exec_helper.execute("sudo mount #{mounts['/']} -t ext3 #{mount_dir}")
|
199
|
+
|
200
|
+
mounts.each { |mount_point, loop_device| @exec_helper.execute("sudo mount #{loop_device} -t ext3 #{mount_dir}/#{mount_point}") unless mount_point == '/' }
|
201
|
+
|
202
|
+
@log.trace "Mounts:\n#{mounts}"
|
203
|
+
|
204
|
+
mounts
|
176
205
|
end
|
177
206
|
|
178
|
-
def umount_image(disk, mount_dir,
|
179
|
-
@log.debug "Unmounting image #{File.basename(disk)}"
|
180
|
-
|
207
|
+
def umount_image(disk, mount_dir, mounts)
|
208
|
+
@log.debug "Unmounting image '#{File.basename(disk)}'..."
|
209
|
+
|
210
|
+
mounts.each { |mount_point, loop_device| @exec_helper.execute("sudo umount -d #{loop_device}") unless mount_point == '/' }
|
211
|
+
|
212
|
+
@exec_helper.execute("sudo umount -d #{mounts['/']}")
|
213
|
+
|
181
214
|
FileUtils.rm_rf(mount_dir)
|
182
215
|
end
|
183
216
|
|
@@ -239,8 +272,11 @@ module BoxGrinder
|
|
239
272
|
"ec2-ami-tools.noarch.rpm" => "http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.noarch.rpm"
|
240
273
|
}
|
241
274
|
|
242
|
-
|
243
|
-
|
275
|
+
begin
|
276
|
+
kernel_rpm = KERNELS[@appliance_config.os.name][@appliance_config.os.version][@appliance_config.hardware.arch][:rpm]
|
277
|
+
rpms[File.basename(kernel_rpm)] = kernel_rpm
|
278
|
+
rescue
|
279
|
+
end
|
244
280
|
|
245
281
|
cache_rpms(rpms)
|
246
282
|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 3
|
8
|
-
-
|
9
|
-
version: 0.3.
|
8
|
+
- 7
|
9
|
+
version: 0.3.7
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- BoxGrinder Project
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-30 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -27,8 +27,8 @@ dependencies:
|
|
27
27
|
segments:
|
28
28
|
- 0
|
29
29
|
- 0
|
30
|
-
-
|
31
|
-
version: 0.0.
|
30
|
+
- 10
|
31
|
+
version: 0.0.10
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
34
|
- !ruby/object:Gem::Dependency
|