vanagon 0.3.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +175 -0
  4. data/bin/build +33 -0
  5. data/bin/devkit +22 -0
  6. data/bin/repo +26 -0
  7. data/bin/ship +15 -0
  8. data/lib/vanagon.rb +8 -0
  9. data/lib/vanagon/common.rb +2 -0
  10. data/lib/vanagon/common/pathname.rb +87 -0
  11. data/lib/vanagon/common/user.rb +25 -0
  12. data/lib/vanagon/component.rb +157 -0
  13. data/lib/vanagon/component/dsl.rb +307 -0
  14. data/lib/vanagon/component/source.rb +66 -0
  15. data/lib/vanagon/component/source/git.rb +60 -0
  16. data/lib/vanagon/component/source/http.rb +158 -0
  17. data/lib/vanagon/driver.rb +112 -0
  18. data/lib/vanagon/engine/base.rb +82 -0
  19. data/lib/vanagon/engine/docker.rb +40 -0
  20. data/lib/vanagon/engine/local.rb +40 -0
  21. data/lib/vanagon/engine/pooler.rb +85 -0
  22. data/lib/vanagon/errors.rb +28 -0
  23. data/lib/vanagon/extensions/string.rb +11 -0
  24. data/lib/vanagon/optparse.rb +62 -0
  25. data/lib/vanagon/platform.rb +245 -0
  26. data/lib/vanagon/platform/deb.rb +71 -0
  27. data/lib/vanagon/platform/dsl.rb +293 -0
  28. data/lib/vanagon/platform/osx.rb +100 -0
  29. data/lib/vanagon/platform/rpm.rb +76 -0
  30. data/lib/vanagon/platform/rpm/wrl.rb +39 -0
  31. data/lib/vanagon/platform/solaris_10.rb +182 -0
  32. data/lib/vanagon/platform/solaris_11.rb +138 -0
  33. data/lib/vanagon/platform/swix.rb +35 -0
  34. data/lib/vanagon/project.rb +251 -0
  35. data/lib/vanagon/project/dsl.rb +218 -0
  36. data/lib/vanagon/utilities.rb +299 -0
  37. data/spec/fixures/component/invalid-test-fixture.json +3 -0
  38. data/spec/fixures/component/mcollective.service +1 -0
  39. data/spec/fixures/component/test-fixture.json +4 -0
  40. data/spec/lib/vanagon/common/pathname_spec.rb +103 -0
  41. data/spec/lib/vanagon/common/user_spec.rb +36 -0
  42. data/spec/lib/vanagon/component/dsl_spec.rb +443 -0
  43. data/spec/lib/vanagon/component/source/git_spec.rb +19 -0
  44. data/spec/lib/vanagon/component/source/http_spec.rb +43 -0
  45. data/spec/lib/vanagon/component/source_spec.rb +99 -0
  46. data/spec/lib/vanagon/component_spec.rb +22 -0
  47. data/spec/lib/vanagon/engine/base_spec.rb +40 -0
  48. data/spec/lib/vanagon/engine/docker_spec.rb +40 -0
  49. data/spec/lib/vanagon/engine/pooler_spec.rb +54 -0
  50. data/spec/lib/vanagon/platform/deb_spec.rb +60 -0
  51. data/spec/lib/vanagon/platform/dsl_spec.rb +128 -0
  52. data/spec/lib/vanagon/platform/rpm_spec.rb +41 -0
  53. data/spec/lib/vanagon/platform/solaris_11_spec.rb +44 -0
  54. data/spec/lib/vanagon/platform_spec.rb +53 -0
  55. data/spec/lib/vanagon/project/dsl_spec.rb +203 -0
  56. data/spec/lib/vanagon/project_spec.rb +44 -0
  57. data/spec/lib/vanagon/utilities_spec.rb +140 -0
  58. data/templates/Makefile.erb +116 -0
  59. data/templates/deb/changelog.erb +5 -0
  60. data/templates/deb/conffiles.erb +3 -0
  61. data/templates/deb/control.erb +21 -0
  62. data/templates/deb/dirs.erb +3 -0
  63. data/templates/deb/docs.erb +1 -0
  64. data/templates/deb/install.erb +3 -0
  65. data/templates/deb/postinst.erb +46 -0
  66. data/templates/deb/postrm.erb +15 -0
  67. data/templates/deb/prerm.erb +17 -0
  68. data/templates/deb/rules.erb +25 -0
  69. data/templates/osx/postinstall.erb +24 -0
  70. data/templates/osx/preinstall.erb +19 -0
  71. data/templates/osx/project-installer.xml.erb +19 -0
  72. data/templates/rpm/project.spec.erb +217 -0
  73. data/templates/solaris/10/depend.erb +3 -0
  74. data/templates/solaris/10/pkginfo.erb +13 -0
  75. data/templates/solaris/10/postinstall.erb +37 -0
  76. data/templates/solaris/10/preinstall.erb +7 -0
  77. data/templates/solaris/10/preremove.erb +6 -0
  78. data/templates/solaris/10/proto.erb +5 -0
  79. data/templates/solaris/11/p5m.erb +73 -0
  80. 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