vanagon 0.3.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/README.md +175 -0
- data/bin/build +33 -0
- data/bin/devkit +22 -0
- data/bin/repo +26 -0
- data/bin/ship +15 -0
- data/lib/vanagon.rb +8 -0
- data/lib/vanagon/common.rb +2 -0
- data/lib/vanagon/common/pathname.rb +87 -0
- data/lib/vanagon/common/user.rb +25 -0
- data/lib/vanagon/component.rb +157 -0
- data/lib/vanagon/component/dsl.rb +307 -0
- data/lib/vanagon/component/source.rb +66 -0
- data/lib/vanagon/component/source/git.rb +60 -0
- data/lib/vanagon/component/source/http.rb +158 -0
- data/lib/vanagon/driver.rb +112 -0
- data/lib/vanagon/engine/base.rb +82 -0
- data/lib/vanagon/engine/docker.rb +40 -0
- data/lib/vanagon/engine/local.rb +40 -0
- data/lib/vanagon/engine/pooler.rb +85 -0
- data/lib/vanagon/errors.rb +28 -0
- data/lib/vanagon/extensions/string.rb +11 -0
- data/lib/vanagon/optparse.rb +62 -0
- data/lib/vanagon/platform.rb +245 -0
- data/lib/vanagon/platform/deb.rb +71 -0
- data/lib/vanagon/platform/dsl.rb +293 -0
- data/lib/vanagon/platform/osx.rb +100 -0
- data/lib/vanagon/platform/rpm.rb +76 -0
- data/lib/vanagon/platform/rpm/wrl.rb +39 -0
- data/lib/vanagon/platform/solaris_10.rb +182 -0
- data/lib/vanagon/platform/solaris_11.rb +138 -0
- data/lib/vanagon/platform/swix.rb +35 -0
- data/lib/vanagon/project.rb +251 -0
- data/lib/vanagon/project/dsl.rb +218 -0
- data/lib/vanagon/utilities.rb +299 -0
- data/spec/fixures/component/invalid-test-fixture.json +3 -0
- data/spec/fixures/component/mcollective.service +1 -0
- data/spec/fixures/component/test-fixture.json +4 -0
- data/spec/lib/vanagon/common/pathname_spec.rb +103 -0
- data/spec/lib/vanagon/common/user_spec.rb +36 -0
- data/spec/lib/vanagon/component/dsl_spec.rb +443 -0
- data/spec/lib/vanagon/component/source/git_spec.rb +19 -0
- data/spec/lib/vanagon/component/source/http_spec.rb +43 -0
- data/spec/lib/vanagon/component/source_spec.rb +99 -0
- data/spec/lib/vanagon/component_spec.rb +22 -0
- data/spec/lib/vanagon/engine/base_spec.rb +40 -0
- data/spec/lib/vanagon/engine/docker_spec.rb +40 -0
- data/spec/lib/vanagon/engine/pooler_spec.rb +54 -0
- data/spec/lib/vanagon/platform/deb_spec.rb +60 -0
- data/spec/lib/vanagon/platform/dsl_spec.rb +128 -0
- data/spec/lib/vanagon/platform/rpm_spec.rb +41 -0
- data/spec/lib/vanagon/platform/solaris_11_spec.rb +44 -0
- data/spec/lib/vanagon/platform_spec.rb +53 -0
- data/spec/lib/vanagon/project/dsl_spec.rb +203 -0
- data/spec/lib/vanagon/project_spec.rb +44 -0
- data/spec/lib/vanagon/utilities_spec.rb +140 -0
- data/templates/Makefile.erb +116 -0
- data/templates/deb/changelog.erb +5 -0
- data/templates/deb/conffiles.erb +3 -0
- data/templates/deb/control.erb +21 -0
- data/templates/deb/dirs.erb +3 -0
- data/templates/deb/docs.erb +1 -0
- data/templates/deb/install.erb +3 -0
- data/templates/deb/postinst.erb +46 -0
- data/templates/deb/postrm.erb +15 -0
- data/templates/deb/prerm.erb +17 -0
- data/templates/deb/rules.erb +25 -0
- data/templates/osx/postinstall.erb +24 -0
- data/templates/osx/preinstall.erb +19 -0
- data/templates/osx/project-installer.xml.erb +19 -0
- data/templates/rpm/project.spec.erb +217 -0
- data/templates/solaris/10/depend.erb +3 -0
- data/templates/solaris/10/pkginfo.erb +13 -0
- data/templates/solaris/10/postinstall.erb +37 -0
- data/templates/solaris/10/preinstall.erb +7 -0
- data/templates/solaris/10/preremove.erb +6 -0
- data/templates/solaris/10/proto.erb +5 -0
- data/templates/solaris/11/p5m.erb +73 -0
- metadata +172 -0
@@ -0,0 +1,307 @@
|
|
1
|
+
require 'vanagon/component'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class Vanagon
|
6
|
+
class Component
|
7
|
+
class DSL
|
8
|
+
# Constructor for the DSL object
|
9
|
+
#
|
10
|
+
# @param name [String] name of the component
|
11
|
+
# @param settings [Hash] settings to use in building the component
|
12
|
+
# @param platform [Vanagon::Platform] platform to build the component for
|
13
|
+
# @return [Vanagon::Component::DSL] A DSL object to describe the {Vanagon::Component}
|
14
|
+
def initialize(name, settings, platform)
|
15
|
+
@name = name
|
16
|
+
@component = Vanagon::Component.new(@name, settings, platform)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Primary way of interacting with the DSL
|
20
|
+
#
|
21
|
+
# @param name [String] name of the componennt
|
22
|
+
# @param block [Proc] DSL definition of the component to call
|
23
|
+
def component(name, &block)
|
24
|
+
block.call(self, @component.settings, @component.platform)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Accessor for the component.
|
28
|
+
#
|
29
|
+
# @return [Vanagon::Component] the component the DSL methods will be acting against
|
30
|
+
def _component
|
31
|
+
@component
|
32
|
+
end
|
33
|
+
|
34
|
+
# All purpose getter. This object, which is passed to the component block,
|
35
|
+
# won't have easy access to the attributes of the @component, so we make a
|
36
|
+
# getter for each attribute.
|
37
|
+
#
|
38
|
+
# We only magically handle get_ methods, any other methods just get the
|
39
|
+
# standard method_missing treatment.
|
40
|
+
#
|
41
|
+
def method_missing(method, *args)
|
42
|
+
attribute_match = method.to_s.match(/get_(.*)/)
|
43
|
+
if attribute_match
|
44
|
+
attribute = attribute_match.captures.first
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
@component.send(attribute)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set or add to the configure call for the component. The commands required to configure the component before building it.
|
53
|
+
#
|
54
|
+
# @param block [Proc] the command(s) required to configure the component
|
55
|
+
def configure(&block)
|
56
|
+
@component.configure << block.call
|
57
|
+
end
|
58
|
+
|
59
|
+
# Set or add to the build call for the component. The commands required to build the component before installing it.
|
60
|
+
#
|
61
|
+
# @param block [Proc] the command(s) required to build the component
|
62
|
+
def build(&block)
|
63
|
+
@component.build << block.call
|
64
|
+
end
|
65
|
+
|
66
|
+
# Set or add to the install call for the component. The commands required to install the component.
|
67
|
+
#
|
68
|
+
# @param block [Proc] the command(s) required to install the component
|
69
|
+
def install(&block)
|
70
|
+
@component.install << block.call
|
71
|
+
end
|
72
|
+
|
73
|
+
# Setup any specific environment required to configure, build or install the component
|
74
|
+
#
|
75
|
+
# @param block [Proc] the environment required to configure, build or install the component
|
76
|
+
def environment(&block)
|
77
|
+
@component.environment = block.call
|
78
|
+
end
|
79
|
+
|
80
|
+
# Add a patch to the list of patches to apply to the component's source after unpacking
|
81
|
+
#
|
82
|
+
# @param patch [String] Path to the patch that should be applied
|
83
|
+
# @param strip [String, Integer] directory levels to skip in applying patch
|
84
|
+
# @param fuzz [String, Integer] levels of context miss to ignore in applying patch
|
85
|
+
def apply_patch(patch, strip: 1, fuzz: 0)
|
86
|
+
@component.patches << OpenStruct.new('path' => patch, 'strip' => strip.to_s, 'fuzz' => fuzz.to_s)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Loads and parses json from a file. Will treat the keys in the
|
90
|
+
# json as methods to invoke on the component in question
|
91
|
+
#
|
92
|
+
# @param file [String] Path to the json file
|
93
|
+
# @raise [RuntimeError] exceptions are raised if there is no file, if it refers to methods that don't exist, or if it does not contain a Hash
|
94
|
+
def load_from_json(file)
|
95
|
+
if File.exists?(file)
|
96
|
+
data = JSON.parse(File.read(file))
|
97
|
+
raise "Hash required. Got '#{data.class}' when parsing '#{file}'" unless data.is_a?(Hash)
|
98
|
+
data.each do |key, value|
|
99
|
+
if self.respond_to?(key)
|
100
|
+
self.send(key, value)
|
101
|
+
else
|
102
|
+
fail "Component does not have a '#{key}' method to invoke. Maybe your bespoke json has a typo?"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
else
|
106
|
+
fail "Cannot load component data from '#{file}'. It does not exist."
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# build_requires adds a requirements to the list of build time dependencies
|
111
|
+
# that will need to be fetched from an external source before this component
|
112
|
+
# can be built. build_requires can also be satisfied by other components in
|
113
|
+
# the same project.
|
114
|
+
#
|
115
|
+
# @param build_requirement [String] a library or other component that is required to build the current component
|
116
|
+
def build_requires(build_requirement)
|
117
|
+
@component.build_requires << build_requirement
|
118
|
+
end
|
119
|
+
|
120
|
+
# requires adds a requirement to the list of runtime requirements for the
|
121
|
+
# component
|
122
|
+
#
|
123
|
+
# @param requirement [String] a package that is required at runtime for this component
|
124
|
+
def requires(requirement)
|
125
|
+
@component.requires << requirement
|
126
|
+
end
|
127
|
+
|
128
|
+
# Indicates that this component replaces a system level package. Replaces can be collected and used by the project and package.
|
129
|
+
#
|
130
|
+
# @param replacement [String] a package that is replaced with this component
|
131
|
+
# @param version [String] the version of the package that is replaced
|
132
|
+
def replaces(replacement, version = nil)
|
133
|
+
@component.replaces << OpenStruct.new(:replacement => replacement, :version => version)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Indicates that this component provides a system level package. Provides can be collected and used by the project and package.
|
137
|
+
#
|
138
|
+
# @param provide [String] a package that is provided with this component
|
139
|
+
# @param version [String] the version of the package that is provided with this component
|
140
|
+
def provides(provide, version = nil)
|
141
|
+
@component.provides << OpenStruct.new(:provide => provide, :version => version)
|
142
|
+
end
|
143
|
+
|
144
|
+
# install_service adds the commands to install the various files on
|
145
|
+
# disk during the package build and registers the service with the project
|
146
|
+
#
|
147
|
+
# @param service_file [String] path to the service file relative to the source
|
148
|
+
# @param default_file [String] path to the default file relative to the source
|
149
|
+
# @param service_name [String] name of the service
|
150
|
+
# @param service_type [String] type of the service (network, application, system, etc)
|
151
|
+
def install_service(service_file, default_file = nil, service_name = @component.name, service_type: nil)
|
152
|
+
case @component.platform.servicetype
|
153
|
+
when "sysv"
|
154
|
+
target_service_file = File.join(@component.platform.servicedir, service_name)
|
155
|
+
target_default_file = File.join(@component.platform.defaultdir, service_name)
|
156
|
+
target_mode = '0755'
|
157
|
+
default_mode = '0644'
|
158
|
+
when "systemd"
|
159
|
+
target_service_file = File.join(@component.platform.servicedir, "#{service_name}.service")
|
160
|
+
target_default_file = File.join(@component.platform.defaultdir, service_name)
|
161
|
+
target_mode = '0644'
|
162
|
+
default_mode = '0644'
|
163
|
+
when "launchd"
|
164
|
+
target_service_file = File.join(@component.platform.servicedir, "#{service_name}.plist")
|
165
|
+
target_mode = '0644'
|
166
|
+
default_mode = '0644'
|
167
|
+
when "smf"
|
168
|
+
target_service_file = File.join(@component.platform.servicedir, service_type.to_s, "#{service_name}.xml")
|
169
|
+
target_default_file = File.join(@component.platform.defaultdir, service_name)
|
170
|
+
target_mode = '0644'
|
171
|
+
default_mode = '0755'
|
172
|
+
when "aix"
|
173
|
+
@component.service = OpenStruct.new(:name => service_name, :service_command => File.read(service_file).chomp)
|
174
|
+
# Return here because there is no file to install, just a string read in
|
175
|
+
return
|
176
|
+
else
|
177
|
+
fail "Don't know how to install the #{@component.platform.servicetype}. Please teach #install_service how to do this."
|
178
|
+
end
|
179
|
+
|
180
|
+
# Install the service and default files
|
181
|
+
install_file(service_file, target_service_file, mode: target_mode)
|
182
|
+
|
183
|
+
if default_file
|
184
|
+
install_file(default_file, target_default_file, mode: default_mode)
|
185
|
+
configfile target_default_file
|
186
|
+
end
|
187
|
+
|
188
|
+
# Register the service for use in packaging
|
189
|
+
@component.service = OpenStruct.new(:name => service_name, :service_file => target_service_file)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Copies a file from source to target during the install phase of the component
|
193
|
+
#
|
194
|
+
# @param source [String] path to the file to copy
|
195
|
+
# @param target [String] path to the desired target of the file
|
196
|
+
# @param owner [String] owner of the file
|
197
|
+
# @param group [String] group owner of the file
|
198
|
+
def install_file(source, target, mode: '0644', owner: nil, group: nil)
|
199
|
+
@component.install << "#{@component.platform.install} -d '#{File.dirname(target)}'"
|
200
|
+
@component.install << "cp -p '#{source}' '#{target}'"
|
201
|
+
@component.add_file Vanagon::Common::Pathname.file(target, mode: mode, owner: owner, group: group)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Marks a file as a configfile to ensure that it is not overwritten on
|
205
|
+
# upgrade if it has been modified
|
206
|
+
#
|
207
|
+
# @param file [String] name of the configfile
|
208
|
+
def configfile(file)
|
209
|
+
# I AM SO SORRY
|
210
|
+
@component.delete_file "#{file}"
|
211
|
+
if @component.platform.name =~ /solaris-10|osx/
|
212
|
+
@component.install << "mv '#{file}' '#{file}.pristine'"
|
213
|
+
@component.add_file Vanagon::Common::Pathname.configfile("#{file}.pristine")
|
214
|
+
else
|
215
|
+
@component.add_file Vanagon::Common::Pathname.configfile(file)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Shorthand to install a file and mark it as a configfile
|
220
|
+
#
|
221
|
+
# @param source [String] path to the configfile to copy
|
222
|
+
# @param target [String] path to the desired target of the configfile
|
223
|
+
def install_configfile(source, target)
|
224
|
+
install_file(source, target)
|
225
|
+
configfile(target)
|
226
|
+
end
|
227
|
+
|
228
|
+
# link will add a command to the install to create a symlink from source to target
|
229
|
+
#
|
230
|
+
# @param source [String] path to the file to symlink
|
231
|
+
# @param target [String] path to the desired symlink
|
232
|
+
def link(source, target)
|
233
|
+
@component.install << "#{@component.platform.install} -d '#{File.dirname(target)}'"
|
234
|
+
@component.install << "ln -s '#{source}' '#{target}'"
|
235
|
+
end
|
236
|
+
|
237
|
+
# Sets the version for the component
|
238
|
+
#
|
239
|
+
# @param ver [String] version of the component
|
240
|
+
def version(ver)
|
241
|
+
@component.version = ver
|
242
|
+
end
|
243
|
+
|
244
|
+
# Sets the url for the source of this component
|
245
|
+
#
|
246
|
+
# @param the_url [String] the url to the source for this component
|
247
|
+
def url(the_url)
|
248
|
+
@component.url = the_url
|
249
|
+
end
|
250
|
+
|
251
|
+
# Sets the md5 sum to verify the sum of the source
|
252
|
+
#
|
253
|
+
# @param md5 [String] md5 sum of the source for verification
|
254
|
+
def md5sum(md5)
|
255
|
+
@component.options[:sum] = md5
|
256
|
+
end
|
257
|
+
|
258
|
+
# Sets the ref of the source for use in a git source
|
259
|
+
#
|
260
|
+
# @param the_ref [String] ref, sha, branch or tag to checkout for a git source
|
261
|
+
def ref(the_ref)
|
262
|
+
@component.options[:ref] = the_ref
|
263
|
+
end
|
264
|
+
|
265
|
+
# This will add a source to the project and put it in the workdir alongside the other sources
|
266
|
+
#
|
267
|
+
# @param url [String] url of the source
|
268
|
+
# @param ref [String] Used for git sources, must be a git ref of some sort
|
269
|
+
# @param sum [String] sum used to validate http and file sources
|
270
|
+
def add_source(url, ref: nil, sum: nil)
|
271
|
+
@component.sources << OpenStruct.new(:url => url, :ref => ref, :sum => sum)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Adds a directory to the list of directories provided by the project, to be included in any packages of the project
|
275
|
+
#
|
276
|
+
# @param dir [String] directory to add to the project
|
277
|
+
# @param mode [String] octal mode to apply to the directory
|
278
|
+
# @param owner [String] owner of the directory
|
279
|
+
# @param group [String] group of the directory
|
280
|
+
def directory(dir, mode: nil, owner: nil, group: nil)
|
281
|
+
@component.directories << Vanagon::Common::Pathname.new(dir, mode: mode, owner: owner, group: group)
|
282
|
+
end
|
283
|
+
|
284
|
+
# Adds a set of environment overrides to the environment for a component.
|
285
|
+
# This environment is included in the configure, build and install steps.
|
286
|
+
#
|
287
|
+
# @param env [Hash] mapping of keys to values to add to the environment for the component
|
288
|
+
def environment(env)
|
289
|
+
@component.environment.merge!(env)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Adds actions to run at the beginning of a package install
|
293
|
+
#
|
294
|
+
# @param action [String] Bourne shell compatible scriptlets to execute
|
295
|
+
def add_preinstall_action(action)
|
296
|
+
@component.preinstall_actions << action
|
297
|
+
end
|
298
|
+
|
299
|
+
# Adds actions to run at the end of a package install
|
300
|
+
#
|
301
|
+
# @param action [String] Bourne shell compatible scriptlets to execute
|
302
|
+
def add_postinstall_action(action)
|
303
|
+
@component.postinstall_actions << action
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'vanagon/component/source/http'
|
2
|
+
require 'vanagon/component/source/git'
|
3
|
+
|
4
|
+
class Vanagon
|
5
|
+
class Component
|
6
|
+
class Source
|
7
|
+
SUPPORTED_PROTOCOLS = ['file', 'http', 'git']
|
8
|
+
@@rewrite_rule = {}
|
9
|
+
|
10
|
+
def self.register_rewrite_rule(protocol, rule)
|
11
|
+
if rule.is_a?(String) or rule.is_a?(Proc)
|
12
|
+
if SUPPORTED_PROTOCOLS.include?(protocol)
|
13
|
+
@@rewrite_rule[protocol] = rule
|
14
|
+
else
|
15
|
+
raise Vanagon::Error, "#{protocol} is not a supported protocol for rewriting"
|
16
|
+
end
|
17
|
+
else
|
18
|
+
raise Vanagon::Error, "String or Proc is required as a rewrite_rule."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.rewrite(url, protocol)
|
23
|
+
rule = @@rewrite_rule[protocol]
|
24
|
+
if rule
|
25
|
+
if rule.is_a?(Proc) and rule.arity == 1
|
26
|
+
return rule.call(url)
|
27
|
+
elsif rule.is_a?(String)
|
28
|
+
target_match = url.match(/.*\/([^\/]*)$/)
|
29
|
+
if target_match
|
30
|
+
target = target_match[1]
|
31
|
+
return File.join(rule, target)
|
32
|
+
else
|
33
|
+
raise Vanagon::Error, "Unable to apply url rewrite to '#{url}', expected to find at least one '/' in the url."
|
34
|
+
end
|
35
|
+
else
|
36
|
+
end
|
37
|
+
else
|
38
|
+
return url
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Basic factory to hand back the correct {Vanagon::Component::Source} subtype to the component
|
43
|
+
#
|
44
|
+
# @param url [String] URL to the source (includes git@... style links)
|
45
|
+
# @param options [Hash] hash of the options needed for the subtype
|
46
|
+
# @param workdir [String] working directory to fetch the source into
|
47
|
+
# @return [Vanagon::Component::Source] the correct subtype for the given source
|
48
|
+
def self.source(url, options, workdir)
|
49
|
+
url_match = url.match(/^(.*)(@|:\/\/)(.*)$/)
|
50
|
+
uri_scheme = url_match[1] if url_match
|
51
|
+
local_source = case uri_scheme
|
52
|
+
when /^http/
|
53
|
+
Vanagon::Component::Source::Http.new(self.rewrite(url, 'http'), options[:sum], workdir)
|
54
|
+
when /^file/
|
55
|
+
Vanagon::Component::Source::Http.new(self.rewrite(url, 'file'), options[:sum], workdir)
|
56
|
+
when /^git/
|
57
|
+
Vanagon::Component::Source::Git.new(self.rewrite(url, 'git'), options[:ref], workdir)
|
58
|
+
else
|
59
|
+
fail "Don't know how to handle source of type '#{uri_scheme}' from url: '#{url}'"
|
60
|
+
end
|
61
|
+
|
62
|
+
return local_source
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'vanagon/utilities'
|
2
|
+
|
3
|
+
class Vanagon
|
4
|
+
class Component
|
5
|
+
class Source
|
6
|
+
class Git
|
7
|
+
include Vanagon::Utilities
|
8
|
+
attr_accessor :url, :ref, :workdir, :version, :cleanup
|
9
|
+
|
10
|
+
# Constructor for the Git source type
|
11
|
+
#
|
12
|
+
# @param url [String] url of git repo to use as source
|
13
|
+
# @param ref [String] ref to checkout from git repo
|
14
|
+
# @param workdir [String] working directory to clone into
|
15
|
+
def initialize(url, ref, workdir)
|
16
|
+
unless ref
|
17
|
+
fail "ref parameter is required for the git source"
|
18
|
+
end
|
19
|
+
@url = url
|
20
|
+
@ref = ref
|
21
|
+
@workdir = workdir
|
22
|
+
end
|
23
|
+
|
24
|
+
# Fetch the source. In this case, clone the repository into the workdir
|
25
|
+
# and check out the ref. Also sets the version if there is a git tag as
|
26
|
+
# a side effect.
|
27
|
+
def fetch
|
28
|
+
puts "Cloning ref '#{@ref}' from url '#{@url}'"
|
29
|
+
Dir.chdir(@workdir) do
|
30
|
+
git('clone', @url)
|
31
|
+
Dir.chdir(dirname) do
|
32
|
+
git('checkout', @ref)
|
33
|
+
git('submodule', 'update', '--init', '--recursive')
|
34
|
+
@version = git_version
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return the correct incantation to cleanup the source directory for a given source
|
40
|
+
#
|
41
|
+
# @return [String] command to cleanup the source
|
42
|
+
def cleanup
|
43
|
+
"rm -rf #{dirname}"
|
44
|
+
end
|
45
|
+
|
46
|
+
# There is no md5 to manually verify here, so it is a noop.
|
47
|
+
def verify
|
48
|
+
# nothing to do here, so just return
|
49
|
+
end
|
50
|
+
|
51
|
+
# The dirname to reference when building from the repo
|
52
|
+
#
|
53
|
+
# @return [String] the directory where the repo was cloned
|
54
|
+
def dirname
|
55
|
+
File.basename(@url).sub(/\.git/, '')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'vanagon/utilities'
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
class Vanagon
|
6
|
+
class Component
|
7
|
+
class Source
|
8
|
+
class Http
|
9
|
+
include Vanagon::Utilities
|
10
|
+
attr_accessor :url, :sum, :file, :extension, :workdir, :cleanup
|
11
|
+
|
12
|
+
# Extensions for files we intend to unpack during the build
|
13
|
+
ARCHIVE_EXTENSIONS = '.tar.gz', '.tgz'
|
14
|
+
|
15
|
+
# Extensions for files we aren't going to unpack during the build
|
16
|
+
NON_ARCHIVE_EXTENSIONS = '.gem', '.ru', '.txt', '.conf', '.ini', '.gpg', '.rb', '.sh', '.csh'
|
17
|
+
|
18
|
+
# Constructor for the Http source type
|
19
|
+
#
|
20
|
+
# @param url [String] url of the http source to fetch
|
21
|
+
# @param sum [String] sum to verify the download against
|
22
|
+
# @param workdir [String] working directory to download into
|
23
|
+
# @raise [RuntimeError] an exception is raised is sum is nil
|
24
|
+
def initialize(url, sum, workdir)
|
25
|
+
unless sum
|
26
|
+
fail "sum is required to validate the http source"
|
27
|
+
end
|
28
|
+
@url = url
|
29
|
+
@sum = sum
|
30
|
+
@workdir = workdir
|
31
|
+
end
|
32
|
+
|
33
|
+
# Download the source from the url specified. Sets the full path to the
|
34
|
+
# file as @file and the @extension for the file as a side effect.
|
35
|
+
def fetch
|
36
|
+
@file = download
|
37
|
+
@extension = get_extension
|
38
|
+
end
|
39
|
+
|
40
|
+
# Verify the downloaded file matches the provided sum
|
41
|
+
#
|
42
|
+
# @raise [RuntimeError] an exception is raised if the sum does not match the sum of the file
|
43
|
+
def verify
|
44
|
+
puts "Verifying file: #{@file} against sum: '#{@sum}'"
|
45
|
+
actual = get_md5sum(File.join(@workdir, @file))
|
46
|
+
unless @sum == actual
|
47
|
+
fail "Unable to verify '#{@file}'. Expected: '#{@sum}', got: '#{actual}'"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Downloads the file from @url into the @workdir
|
52
|
+
#
|
53
|
+
# @raise [RuntimeError, Vanagon::Error] an exception is raised if the URI scheme cannot be handled
|
54
|
+
def download
|
55
|
+
uri = URI.parse(@url)
|
56
|
+
target_file = File.basename(uri.path)
|
57
|
+
puts "Downloading file '#{target_file}' from url '#{@url}'"
|
58
|
+
|
59
|
+
case uri.scheme
|
60
|
+
when 'http', 'https'
|
61
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
|
62
|
+
request = Net::HTTP::Get.new(uri)
|
63
|
+
|
64
|
+
http.request request do |response|
|
65
|
+
unless response.is_a? Net::HTTPSuccess
|
66
|
+
fail "Error: #{response.code.to_s}. Unable to get source from #{@url}"
|
67
|
+
end
|
68
|
+
open(File.join(@workdir, target_file), 'w') do |io|
|
69
|
+
response.read_body do |chunk|
|
70
|
+
io.write(chunk)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
when 'file'
|
76
|
+
uri = @url.match(/^file:\/\/(.*)$/)
|
77
|
+
if uri
|
78
|
+
source_file = uri[1]
|
79
|
+
target_file = File.basename(source_file)
|
80
|
+
FileUtils.cp(source_file, File.join(@workdir, target_file))
|
81
|
+
else
|
82
|
+
raise Vanagon::Error, "Unable to parse '#{@url}' for local file path."
|
83
|
+
end
|
84
|
+
else
|
85
|
+
fail "Unable to download files using the uri scheme '#{uri.scheme}'. Maybe you have a typo or need to teach me a new trick?"
|
86
|
+
end
|
87
|
+
|
88
|
+
target_file
|
89
|
+
|
90
|
+
rescue Errno::ETIMEDOUT, Timeout::Error, Errno::EINVAL,
|
91
|
+
Errno::EACCES, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse,
|
92
|
+
Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
|
93
|
+
raise Vanagon::Error.wrap(e, "Problem downloading #{target_file} from '#{@url}'. Please verify you have the correct uri specified.")
|
94
|
+
end
|
95
|
+
|
96
|
+
# Gets the command to extract the archive given if needed (uses @extension)
|
97
|
+
#
|
98
|
+
# @param tar [String] the tar command to use
|
99
|
+
# @return [String, nil] command to extract the source
|
100
|
+
# @raise [RuntimeError] an exception is raised if there is no known extraction method for @extension
|
101
|
+
def extract(tar)
|
102
|
+
if ARCHIVE_EXTENSIONS.include?(@extension)
|
103
|
+
return "gunzip -c '#{@file}' | '#{tar}' xf -"
|
104
|
+
elsif NON_ARCHIVE_EXTENSIONS.include?(@extension)
|
105
|
+
# Don't need to unpack gems, ru, txt, conf, ini, gpg
|
106
|
+
return nil
|
107
|
+
else
|
108
|
+
fail "Extraction unimplemented for '#{@extension}' in source '#{@file}'. Please teach me."
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return the correct incantation to cleanup the source archive and source directory for a given source
|
113
|
+
#
|
114
|
+
# @return [String] command to cleanup the source
|
115
|
+
# @raise [RuntimeError] an exception is raised if there is no known extraction method for @extension
|
116
|
+
def cleanup
|
117
|
+
if ARCHIVE_EXTENSIONS.include?(@extension)
|
118
|
+
return "rm #{@file}; rm -r #{dirname}"
|
119
|
+
elsif NON_ARCHIVE_EXTENSIONS.include?(@extension)
|
120
|
+
# Because dirname will be ./ here, we don't want to try to nuke it
|
121
|
+
return "rm #{@file}"
|
122
|
+
else
|
123
|
+
fail "Don't know how to cleanup for '#{@file}' with extension: '#{@extension}'. Please teach me."
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns the extension for @file
|
128
|
+
#
|
129
|
+
# @return [String] the extension of @file
|
130
|
+
# @raise [RuntimeError] an exception is raised if the extension isn't in the current list
|
131
|
+
def get_extension
|
132
|
+
extension_match = @file.match(/.*(#{Regexp.union(ARCHIVE_EXTENSIONS + NON_ARCHIVE_EXTENSIONS)})/)
|
133
|
+
unless extension_match
|
134
|
+
fail "Unrecognized extension for '#{@file}'. Don't know how to extract this format. Please teach me."
|
135
|
+
end
|
136
|
+
|
137
|
+
extension_match[1]
|
138
|
+
end
|
139
|
+
|
140
|
+
# The dirname to reference when building from the source
|
141
|
+
#
|
142
|
+
# @return [String] the directory that should be traversed into to build this source
|
143
|
+
# @raise [RuntimeError] if the @extension for the @file isn't currently handled by the method
|
144
|
+
def dirname
|
145
|
+
if ARCHIVE_EXTENSIONS.include?(@extension)
|
146
|
+
return @file.chomp(@extension)
|
147
|
+
elsif NON_ARCHIVE_EXTENSIONS.include?(@extension)
|
148
|
+
# Because we cd into the source dir, using ./ here avoids special casing single file
|
149
|
+
# sources in the Makefile
|
150
|
+
return './'
|
151
|
+
else
|
152
|
+
fail "Don't know how to guess dirname for '#{@file}' with extension: '#{@extension}'. Please teach me."
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|