fig 0.1.62 → 0.1.64

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/Changes +156 -0
  2. data/VERSION +1 -1
  3. data/bin/fig +9 -2
  4. data/bin/fig-debug +9 -2
  5. data/lib/fig/applicationconfiguration.rb +3 -2
  6. data/lib/fig/atexit.rb +37 -0
  7. data/lib/fig/backtrace.rb +23 -6
  8. data/lib/fig/command.rb +131 -31
  9. data/lib/fig/command/coveragesupport.rb +40 -0
  10. data/lib/fig/command/listing.rb +8 -8
  11. data/lib/fig/command/optionerror.rb +8 -0
  12. data/lib/fig/{options.rb → command/options.rb} +248 -144
  13. data/lib/fig/command/packageload.rb +161 -62
  14. data/lib/fig/configfileerror.rb +2 -0
  15. data/lib/fig/environment.rb +350 -246
  16. data/lib/fig/environmentvariables/casesensitive.rb +1 -1
  17. data/lib/fig/figrc.rb +78 -78
  18. data/lib/fig/grammar.treetop +204 -219
  19. data/lib/fig/log4rconfigerror.rb +2 -0
  20. data/lib/fig/operatingsystem.rb +382 -334
  21. data/lib/fig/package.rb +11 -33
  22. data/lib/fig/packagecache.rb +1 -1
  23. data/lib/fig/packagedescriptor.rb +103 -21
  24. data/lib/fig/packagedescriptorparseerror.rb +16 -0
  25. data/lib/fig/parser.rb +36 -19
  26. data/lib/fig/parserpackagebuildstate.rb +56 -0
  27. data/lib/fig/repository.rb +504 -259
  28. data/lib/fig/statement.rb +30 -12
  29. data/lib/fig/statement/archive.rb +8 -5
  30. data/lib/fig/statement/asset.rb +19 -0
  31. data/lib/fig/statement/command.rb +2 -2
  32. data/lib/fig/statement/configuration.rb +20 -20
  33. data/lib/fig/statement/include.rb +13 -34
  34. data/lib/fig/statement/override.rb +21 -7
  35. data/lib/fig/statement/path.rb +22 -2
  36. data/lib/fig/statement/resource.rb +14 -4
  37. data/lib/fig/statement/retrieve.rb +34 -4
  38. data/lib/fig/statement/set.rb +22 -2
  39. data/lib/fig/workingdirectorymaintainer.rb +197 -0
  40. data/lib/fig/workingdirectorymetadata.rb +45 -0
  41. metadata +52 -46
  42. data/lib/fig/retriever.rb +0 -141
  43. data/lib/fig/statement/publish.rb +0 -15
@@ -3,6 +3,8 @@ require 'fig/userinputerror'
3
3
  module Fig
4
4
  # A problem with configuring Log4r.
5
5
  class Log4rConfigError < UserInputError
6
+ attr_accessor :config_file, :original_exception
7
+
6
8
  def initialize(config_file, original_exception)
7
9
  super(
8
10
  %Q<Problem with #{config_file}: #{original_exception.message}>
@@ -13,420 +13,468 @@ require 'uri'
13
13
 
14
14
  require 'highline/import'
15
15
 
16
+ require 'fig/atexit'
16
17
  require 'fig/environmentvariables/caseinsensitive'
17
18
  require 'fig/environmentvariables/casesensitive'
18
19
  require 'fig/logging'
19
20
  require 'fig/networkerror'
20
21
  require 'fig/notfounderror'
21
22
 
22
- module Fig
23
- # Does things requiring real O/S interaction, primarilly taking care of file
24
- # transfers and running external commands.
25
- class OperatingSystem
26
- def initialize(login)
27
- @login = login
28
- @username = ENV['FIG_USERNAME']
29
- @password = ENV['FIG_PASSWORD']
30
- end
23
+ module Fig; end
31
24
 
32
- def get_username()
33
- @username ||= ask('Username: ') { |q| q.echo = true }
34
- end
25
+ # Does things requiring real O/S interaction, primarilly taking care of file
26
+ # transfers and running external commands.
27
+ class Fig::OperatingSystem
28
+ def initialize(login)
29
+ @login = login
30
+ @username = ENV['FIG_USERNAME']
31
+ @password = ENV['FIG_PASSWORD']
32
+ end
35
33
 
36
- def get_password()
37
- @password ||= ask('Password: ') { |q| q.echo = false }
38
- end
34
+ def get_username()
35
+ @username ||= ask('Username: ') { |q| q.echo = true }
36
+ end
39
37
 
40
- def ftp_login(ftp, host)
41
- if @login
42
- rc = Net::Netrc.locate(host)
43
- if rc
44
- @username = rc.login
45
- @password = rc.password
46
- end
47
- ftp.login(get_username, get_password)
48
- else
49
- ftp.login()
38
+ def get_password()
39
+ @password ||= ask('Password: ') { |q| q.echo = false }
40
+ end
41
+
42
+ def ftp_login(ftp, host)
43
+ if @login
44
+ rc = Net::Netrc.locate(host)
45
+ if rc
46
+ @username = rc.login
47
+ @password = rc.password
50
48
  end
51
- ftp.passive = true
49
+ ftp.login(get_username, get_password)
50
+ else
51
+ ftp.login()
52
52
  end
53
+ ftp.passive = true
54
+ end
53
55
 
54
- def list(dir)
55
- Dir.entries(dir) - ['.','..']
56
- end
56
+ def list(dir)
57
+ Dir.entries(dir) - ['.','..']
58
+ end
57
59
 
58
- def mtime(path)
59
- File.mtime(path)
60
- end
60
+ def mtime(path)
61
+ File.mtime(path)
62
+ end
61
63
 
62
- def write(path, content)
63
- File.open(path, 'wb') { |f| f.binmode; f << content }
64
- end
64
+ def write(path, content)
65
+ File.open(path, 'wb') { |f| f.binmode; f << content }
66
+ end
65
67
 
66
- SUCCESS = 0
67
- NOT_MODIFIED = 3
68
- NOT_FOUND = 4
68
+ SUCCESS = 0
69
+ NOT_MODIFIED = 3
70
+ NOT_FOUND = 4
69
71
 
70
- def strip_paths_for_list(ls_output, packages, path)
71
- if not ls_output.nil?
72
- ls_output = ls_output.gsub(path + '/', '').gsub(path, '').split("\n")
73
- ls_output.each do |line|
74
- parts = line.gsub(/\\/, '/').sub(/^\.\//, '').sub(/:$/, '').chomp().split('/')
75
- packages << parts.join('/') if parts.size == 2
76
- end
72
+ def strip_paths_for_list(ls_output, packages, path)
73
+ if not ls_output.nil?
74
+ ls_output = ls_output.gsub(path + '/', '').gsub(path, '').split("\n")
75
+ ls_output.each do |line|
76
+ parts = line.gsub(/\\/, '/').sub(/^\.\//, '').sub(/:$/, '').chomp().split('/')
77
+ packages << parts.join('/') if parts.size == 2
77
78
  end
78
79
  end
80
+ end
79
81
 
80
- def download_list(url)
81
- begin
82
- uri = URI.parse(url)
83
- rescue
84
- Logging.fatal %Q<Unable to parse url: "#{url}">
85
- raise NetworkError.new
86
- end
87
- case uri.scheme
88
- when 'ftp'
89
- ftp = Net::FTP.new(uri.host)
90
- ftp_login(ftp, uri.host)
91
- ftp.chdir(uri.path)
92
- dirs = ftp.nlst
93
- ftp.close
94
-
95
- download_ftp_list(uri, dirs)
96
- when 'ssh'
97
- packages = []
98
- Net::SSH.start(uri.host, uri.user) do |ssh|
99
- ls = ssh.exec!("[ -d #{uri.path} ] && find #{uri.path}")
100
- strip_paths_for_list(ls, packages, uri.path)
101
- end
102
- packages
103
- when 'file'
104
- packages = []
105
- return packages if ! File.exist?(uri.path)
106
-
107
- ls = ''
108
- Find.find(uri.path) { |file| ls << file.to_s; ls << "\n" }
109
-
82
+ def download_list(url)
83
+ begin
84
+ uri = URI.parse(url)
85
+ rescue
86
+ Fig::Logging.fatal %Q<Unable to parse url: "#{url}">
87
+ raise Fig::NetworkError.new
88
+ end
89
+ case uri.scheme
90
+ when 'ftp'
91
+ ftp = Net::FTP.new(uri.host)
92
+ ftp_login(ftp, uri.host)
93
+ ftp.chdir(uri.path)
94
+ dirs = ftp.nlst
95
+ ftp.close
96
+
97
+ download_ftp_list(uri, dirs)
98
+ when 'ssh'
99
+ packages = []
100
+ Net::SSH.start(uri.host, uri.user) do |ssh|
101
+ ls = ssh.exec!("[ -d #{uri.path} ] && find #{uri.path}")
110
102
  strip_paths_for_list(ls, packages, uri.path)
111
- return packages
112
- else
113
- Logging.fatal "Protocol not supported: #{url}"
114
- raise NetworkError.new("Protocol not supported: #{url}")
115
103
  end
104
+ packages
105
+ when 'file'
106
+ packages = []
107
+ return packages if ! File.exist?(uri.path)
108
+
109
+ ls = ''
110
+ Find.find(uri.path) { |file| ls << file.to_s; ls << "\n" }
111
+
112
+ strip_paths_for_list(ls, packages, uri.path)
113
+ return packages
114
+ else
115
+ Fig::Logging.fatal "Protocol not supported: #{url}"
116
+ raise Fig::NetworkError.new("Protocol not supported: #{url}")
116
117
  end
118
+ end
117
119
 
118
- def download_ftp_list(uri, dirs)
119
- # Run a bunch of these in parallel since they're slow as hell
120
- num_threads = (ENV['FIG_FTP_THREADS'] || '16').to_i
121
- threads = []
122
- all_packages = []
123
- (0..num_threads-1).each { |num| all_packages[num] = [] }
124
- (0..num_threads-1).each do |num|
125
- threads << Thread.new do
126
- packages = all_packages[num]
127
- ftp = Net::FTP.new(uri.host)
128
- ftp_login(ftp, uri.host)
129
- ftp.chdir(uri.path)
130
- pos = num
131
- while pos < dirs.length
132
- pkg = dirs[pos]
133
- begin
134
- ftp.nlst(dirs[pos]).each do |ver|
135
- packages << pkg + '/' + ver
136
- end
137
- rescue Net::FTPPermError
138
- # Ignore this error because it's indicative of the FTP library encountering a file
139
- # or directory that it does not have permission to open.
140
- # Fig needs to be able to have secure repos/packages
141
- # and there is no way easy way to deal with the permissions issues other than consuming these errors.
120
+ def download_ftp_list(uri, dirs)
121
+ # Run a bunch of these in parallel since they're slow as hell
122
+ num_threads = (ENV['FIG_FTP_THREADS'] || '16').to_i
123
+ threads = []
124
+ all_packages = []
125
+ (0..num_threads-1).each { |num| all_packages[num] = [] }
126
+ (0..num_threads-1).each do |num|
127
+ threads << Thread.new do
128
+ packages = all_packages[num]
129
+ ftp = Net::FTP.new(uri.host)
130
+ ftp_login(ftp, uri.host)
131
+ ftp.chdir(uri.path)
132
+ pos = num
133
+ while pos < dirs.length
134
+ pkg = dirs[pos]
135
+ begin
136
+ ftp.nlst(dirs[pos]).each do |ver|
137
+ packages << pkg + '/' + ver
142
138
  end
143
- pos += num_threads
139
+ rescue Net::FTPPermError
140
+ # Ignore this error because it's indicative of the FTP library
141
+ # encountering a file or directory that it does not have
142
+ # permission to open. Fig needs to be able to have secure
143
+ # repos/packages and there is no way easy way to deal with the
144
+ # permissions issues other than consuming these errors.
145
+ #
146
+ # Actually, with FTP, you can't tell the difference between a
147
+ # file not existing and not having permission to access it (which
148
+ # is probably a good thing).
144
149
  end
145
- ftp.close
150
+ pos += num_threads
146
151
  end
152
+ ftp.close
147
153
  end
148
- threads.each { |thread| thread.join }
149
- all_packages.flatten.sort
150
154
  end
155
+ threads.each { |thread| thread.join }
156
+ all_packages.flatten.sort
157
+ end
151
158
 
152
- def download(url, path)
153
- FileUtils.mkdir_p(File.dirname(path))
154
- uri = URI.parse(url)
155
- case uri.scheme
156
- when 'ftp'
157
- begin
158
- ftp = Net::FTP.new(uri.host)
159
- ftp_login(ftp, uri.host)
159
+ # Returns whether the file was not downloaded because the file already
160
+ # exists and is already up-to-date.
161
+ def download(url, path)
162
+ FileUtils.mkdir_p(File.dirname(path))
163
+ uri = URI.parse(url)
164
+ case uri.scheme
165
+ when 'ftp'
166
+ begin
167
+ ftp = Net::FTP.new(uri.host)
168
+ ftp_login(ftp, uri.host)
160
169
 
161
- if File.exist?(path) && ftp.mtime(uri.path) <= File.mtime(path)
162
- Logging.debug "#{path} is up to date."
163
- return false
164
- else
165
- log_download(url, path)
166
- ftp.getbinaryfile(uri.path, path, 256*1024)
167
- return true
168
- end
169
- rescue Net::FTPPermError => error
170
- Logging.warn error.message
171
- raise NotFoundError.new
172
- rescue SocketError => error
173
- Logging.warn error.message
174
- raise NotFoundError.new
175
- end
176
- when 'http'
177
- http = Net::HTTP.new(uri.host)
178
- log_download(url, path)
179
- File.open(path, 'wb') do |file|
180
- file.binmode
181
- http.get(uri.path) do |block|
182
- file.write(block)
183
- end
170
+ if File.exist?(path) && ftp.mtime(uri.path) <= File.mtime(path)
171
+ Fig::Logging.debug "#{path} is up to date."
172
+ return false
173
+ else
174
+ log_download(url, path)
175
+ ftp.getbinaryfile(uri.path, path, 256*1024)
176
+ return true
184
177
  end
185
- when 'ssh'
186
- # TODO need better way to do conditional download
187
- timestamp = File.exist?(path) ? File.mtime(path).to_i : 0
188
- # Requires that remote installation of fig be at the same location as the local machine.
189
- cmd = `which fig-download`.strip + " #{timestamp} #{uri.path}"
190
- log_download(url, path)
191
- ssh_download(uri.user, uri.host, path, cmd)
192
- when 'file'
178
+ rescue Net::FTPPermError => error
179
+ Fig::Logging.debug error.message
180
+ raise Fig::NotFoundError.new
181
+ rescue SocketError => error
182
+ Fig::Logging.debug error.message
183
+ raise Fig::NotFoundError.new
184
+ end
185
+ when 'http'
186
+ log_download(url, path)
187
+ File.open(path, 'wb') do |file|
188
+ file.binmode
189
+
193
190
  begin
194
- FileUtils.cp(uri.path, path)
195
- return true
196
- rescue Errno::ENOENT
197
- raise NotFoundError.new
191
+ download_via_http_get(url, file)
192
+ rescue SystemCallError => error
193
+ Fig::Logging.debug error.message
194
+ raise Fig::NotFoundError.new
195
+ rescue SocketError => error
196
+ Fig::Logging.debug error.message
197
+ raise Fig::NotFoundError.new
198
198
  end
199
- else
200
- Logging.fatal "Unknown protocol: #{url}"
201
- raise NetworkError.new("Unknown protocol: #{url}")
202
199
  end
200
+ when 'ssh'
201
+ # TODO need better way to do conditional download
202
+ timestamp = File.exist?(path) ? File.mtime(path).to_i : 0
203
+ # Requires that remote installation of fig be at the same location as the local machine.
204
+ cmd = `which fig-download`.strip + " #{timestamp} #{uri.path}"
205
+ log_download(url, path)
206
+ ssh_download(uri.user, uri.host, path, cmd)
207
+ when 'file'
208
+ begin
209
+ FileUtils.cp(uri.path, path)
210
+ return true
211
+ rescue Errno::ENOENT
212
+ raise Fig::NotFoundError.new
213
+ end
214
+ else
215
+ Fig::Logging.fatal "Unknown protocol: #{url}"
216
+ raise Fig::NetworkError.new("Unknown protocol: #{url}")
203
217
  end
218
+ end
204
219
 
205
- def download_resource(url, dir)
206
- FileUtils.mkdir_p(dir)
207
- download(url, File.join(dir, URI.parse(url).path.split('/').last))
208
- end
220
+ def download_resource(url, dir)
221
+ FileUtils.mkdir_p(dir)
222
+ download(url, File.join(dir, URI.parse(url).path.split('/').last))
223
+ end
209
224
 
210
- def download_archive(url, dir)
211
- FileUtils.mkdir_p(dir)
212
- basename = URI.parse(url).path.split('/').last
213
- path = File.join(dir, basename)
214
- download(url, path)
215
- case basename
216
- when /\.tar\.gz$/
217
- unpack_archive(dir, path)
218
- when /\.tgz$/
219
- unpack_archive(dir, path)
220
- when /\.tar\.bz2$/
221
- unpack_archive(dir, path)
222
- when /\.zip$/
223
- unpack_archive(dir, path)
224
- else
225
- Logging.fatal "Unknown archive type: #{basename}"
226
- raise NetworkError.new("Unknown archive type: #{basename}")
227
- end
225
+ def download_and_unpack_archive(url, dir)
226
+ FileUtils.mkdir_p(dir)
227
+ basename = URI.parse(url).path.split('/').last
228
+ path = File.join(dir, basename)
229
+ download(url, path)
230
+ case basename
231
+ when /\.tar\.gz$/
232
+ unpack_archive(dir, path)
233
+ when /\.tgz$/
234
+ unpack_archive(dir, path)
235
+ when /\.tar\.bz2$/
236
+ unpack_archive(dir, path)
237
+ when /\.zip$/
238
+ unpack_archive(dir, path)
239
+ else
240
+ Fig::Logging.fatal "Unknown archive type: #{basename}"
241
+ raise Fig::NetworkError.new("Unknown archive type: #{basename}")
228
242
  end
243
+ end
229
244
 
230
- def upload(local_file, remote_file, user)
231
- Logging.debug "Uploading #{local_file} to #{remote_file}."
232
- uri = URI.parse(remote_file)
233
- case uri.scheme
234
- when 'ssh'
235
- ssh_upload(uri.user, uri.host, local_file, remote_file)
236
- when 'ftp'
237
- # fail unless system "curl -T #{local_file} --create-dirs --ftp-create-dirs #{remote_file}"
238
- require 'net/ftp'
239
- ftp_uri = URI.parse(ENV['FIG_REMOTE_URL'])
240
- ftp_root_path = ftp_uri.path
241
- ftp_root_dirs = ftp_uri.path.split('/')
242
- remote_publish_path = uri.path[0, uri.path.rindex('/')]
243
- remote_publish_dirs = remote_publish_path.split('/')
244
- # Use array subtraction to deduce which project/version folder to upload to,
245
- # i.e. [1,2,3] - [2,3,4] = [1]
246
- remote_project_dirs = remote_publish_dirs - ftp_root_dirs
247
- Net::FTP.open(uri.host) do |ftp|
248
- ftp_login(ftp, uri.host)
249
- # Assume that the FIG_REMOTE_URL path exists.
250
- ftp.chdir(ftp_root_path)
251
- remote_project_dirs.each do |dir|
252
- # Can't automatically create parent directories, so do it manually.
253
- if ftp.nlst().index(dir).nil?
254
- ftp.mkdir(dir)
255
- ftp.chdir(dir)
256
- else
257
- ftp.chdir(dir)
258
- end
245
+ def upload(local_file, remote_file, user)
246
+ Fig::Logging.debug "Uploading #{local_file} to #{remote_file}."
247
+ uri = URI.parse(remote_file)
248
+ case uri.scheme
249
+ when 'ssh'
250
+ ssh_upload(uri.user, uri.host, local_file, remote_file)
251
+ when 'ftp'
252
+ # fail unless system "curl -T #{local_file} --create-dirs --ftp-create-dirs #{remote_file}"
253
+ require 'net/ftp'
254
+ ftp_uri = URI.parse(ENV['FIG_REMOTE_URL'])
255
+ ftp_root_path = ftp_uri.path
256
+ ftp_root_dirs = ftp_uri.path.split('/')
257
+ remote_publish_path = uri.path[0, uri.path.rindex('/')]
258
+ remote_publish_dirs = remote_publish_path.split('/')
259
+ # Use array subtraction to deduce which project/version folder to upload to,
260
+ # i.e. [1,2,3] - [2,3,4] = [1]
261
+ remote_project_dirs = remote_publish_dirs - ftp_root_dirs
262
+ Net::FTP.open(uri.host) do |ftp|
263
+ ftp_login(ftp, uri.host)
264
+ # Assume that the FIG_REMOTE_URL path exists.
265
+ ftp.chdir(ftp_root_path)
266
+ remote_project_dirs.each do |dir|
267
+ # Can't automatically create parent directories, so do it manually.
268
+ if ftp.nlst().index(dir).nil?
269
+ ftp.mkdir(dir)
270
+ ftp.chdir(dir)
271
+ else
272
+ ftp.chdir(dir)
259
273
  end
260
- ftp.putbinaryfile(local_file)
261
274
  end
262
- when 'file'
263
- FileUtils.mkdir_p(File.dirname(uri.path))
264
- FileUtils.cp(local_file, uri.path)
265
- else
266
- Logging.fatal "Unknown protocol: #{uri}"
267
- raise NetworkError.new("Unknown protocol: #{uri}")
275
+ ftp.putbinaryfile(local_file)
268
276
  end
277
+ when 'file'
278
+ FileUtils.mkdir_p(File.dirname(uri.path))
279
+ FileUtils.cp(local_file, uri.path)
280
+ else
281
+ Fig::Logging.fatal "Unknown protocol: #{uri}"
282
+ raise Fig::NetworkError.new("Unknown protocol: #{uri}")
269
283
  end
284
+ end
270
285
 
271
- def clear_directory(dir)
272
- FileUtils.rm_rf(dir)
273
- FileUtils.mkdir_p(dir)
274
- end
286
+ def delete_and_recreate_directory(dir)
287
+ FileUtils.rm_rf(dir)
288
+ FileUtils.mkdir_p(dir)
289
+ end
275
290
 
276
- def copy(source, target, msg = nil)
277
- if File.directory?(source)
278
- FileUtils.mkdir_p(target)
279
- Dir.foreach(source) do |child|
280
- if child != '.' and child != '..'
281
- copy(File.join(source, child), File.join(target, child), msg)
282
- end
283
- end
284
- else
285
- if !File.exist?(target) || File.mtime(source) != File.mtime(target)
286
- log_info "#{msg} #{target}" if msg
287
- FileUtils.mkdir_p(File.dirname(target))
288
- FileUtils.cp(source, target)
289
- File.utime(File.atime(source), File.mtime(source), target)
291
+ def copy(source, target, msg = nil)
292
+ if File.directory?(source)
293
+ FileUtils.mkdir_p(target)
294
+ Dir.foreach(source) do |child|
295
+ if child != '.' and child != '..'
296
+ copy(File.join(source, child), File.join(target, child), msg)
290
297
  end
291
298
  end
299
+ else
300
+ if !File.exist?(target) || File.mtime(source) != File.mtime(target)
301
+ log_info "#{msg} #{target}" if msg
302
+ FileUtils.mkdir_p(File.dirname(target))
303
+ FileUtils.cp(source, target)
304
+ File.utime(File.atime(source), File.mtime(source), target)
305
+ end
292
306
  end
307
+ end
293
308
 
294
- def move_file(dir, from, to)
295
- Dir.chdir(dir) { FileUtils.mv(from, to, :force => true) }
296
- end
309
+ def move_file(dir, from, to)
310
+ Dir.chdir(dir) { FileUtils.mv(from, to, :force => true) }
311
+ end
297
312
 
298
- def log_info(msg)
299
- Logging.info msg
300
- end
313
+ def log_info(msg)
314
+ Fig::Logging.info msg
315
+ end
301
316
 
302
- # Expects files_to_archive as an Array of filenames.
303
- def create_archive(archive_name, files_to_archive)
304
- if OperatingSystem.java?
305
- `tar czvf #{archive_name} #{files_to_archive.join(' ')}`
306
- else
307
- # TODO: Need to verify files_to_archive exists.
308
- ::Archive.write_open_filename(archive_name, ::Archive::COMPRESSION_GZIP, ::Archive::FORMAT_TAR) do |ar|
309
- files_to_archive.each do |fn|
310
- ar.new_entry do |entry|
311
- entry.copy_stat(fn)
312
- entry.pathname = fn
313
- ar.write_header(entry)
314
- if !entry.directory?
315
- ar.write_data(open(fn) {|f| f.binmode; f.read })
316
- end
317
+ # Expects files_to_archive as an Array of filenames.
318
+ def create_archive(archive_name, files_to_archive)
319
+ if Fig::OperatingSystem.java?
320
+ `tar czvf #{archive_name} #{files_to_archive.join(' ')}`
321
+ else
322
+ # TODO: Need to verify files_to_archive exists.
323
+ ::Archive.write_open_filename(
324
+ archive_name, ::Archive::COMPRESSION_GZIP, ::Archive::FORMAT_TAR
325
+ ) do |writer|
326
+ files_to_archive.each do |file_name|
327
+ writer.new_entry do |entry|
328
+ entry.copy_lstat(file_name)
329
+ entry.pathname = file_name
330
+ if entry.symbolic_link?
331
+ linked = File.readlink(file_name)
332
+ entry.symlink = linked
333
+ end
334
+ writer.write_header(entry)
335
+
336
+ if entry.regular?
337
+ writer.write_data(open(file_name) {|f| f.binmode; f.read })
317
338
  end
318
339
  end
319
340
  end
320
341
  end
321
342
  end
343
+ end
322
344
 
323
- # This method can handle the following archive types:
324
- # .tar.bz2
325
- # .tar.gz
326
- # .tgz
327
- # .zip
328
- def unpack_archive(dir, file)
329
- Dir.chdir(dir) do
330
- if OperatingSystem.java?
331
- `tar xzvf #{file}`
332
- else
333
- ::Archive.read_open_filename(file) do |ar|
334
- while entry = ar.next_header
335
- ar.extract(entry)
336
- end
345
+ # This method can handle the following archive types:
346
+ # .tar.bz2
347
+ # .tar.gz
348
+ # .tgz
349
+ # .zip
350
+ def unpack_archive(dir, file)
351
+ Dir.chdir(dir) do
352
+ if Fig::OperatingSystem.java?
353
+ `tar xzvf #{file}`
354
+ else
355
+ ::Archive.read_open_filename(file) do |reader|
356
+ while entry = reader.next_header
357
+ reader.extract(entry)
337
358
  end
338
359
  end
339
360
  end
340
361
  end
362
+ end
341
363
 
342
- def self.windows?
343
- RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
344
- end
364
+ def self.windows?
365
+ RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
366
+ end
367
+
368
+ def self.java?
369
+ RUBY_PLATFORM == 'java'
370
+ end
345
371
 
346
- def self.java?
347
- RUBY_PLATFORM == 'java'
372
+ def self.unix?
373
+ !windows?
374
+ end
375
+
376
+ def shell_exec(cmd)
377
+ # Kernel#exec won't run Kernel#at_exit handlers.
378
+ Fig::AtExit.execute()
379
+ if ENV['FIG_COVERAGE']
380
+ SimpleCov.at_exit.call
348
381
  end
349
382
 
350
- def self.unix?
351
- !windows?
383
+ if Fig::OperatingSystem.windows?
384
+ Kernel.exec(ENV['ComSpec'], '/c', cmd.join(' '))
385
+ else
386
+ Kernel.exec(ENV['SHELL'], '-c', cmd.join(' '))
352
387
  end
388
+ end
353
389
 
354
- def shell_exec(cmd)
355
- if OperatingSystem.windows?
356
- Kernel.exec(ENV['ComSpec'], '/c', cmd.join(' '))
357
- else
358
- Kernel.exec(ENV['SHELL'], '-c', cmd.join(' '))
359
- end
390
+ def self.wrap_variable_name_with_shell_expansion(variable_name)
391
+ if Fig::OperatingSystem.windows?
392
+ return "%#{variable_name}%"
393
+ else
394
+ return "$#{variable_name}"
360
395
  end
396
+ end
361
397
 
362
- def self.add_shell_variable_expansion(variable_name)
363
- if OperatingSystem.windows?
364
- return "%#{variable_name}%"
365
- else
366
- return "$#{variable_name}"
367
- end
398
+ def self.get_environment_variables(initial_values = nil)
399
+ if Fig::OperatingSystem.windows?
400
+ return Fig::EnvironmentVariables::CaseInsensitive.new(initial_values)
368
401
  end
369
402
 
370
- def self.get_environment_variables(initial_values = nil)
371
- if OperatingSystem.windows?
372
- return EnvironmentVariables::CaseInsensitive.new(initial_values)
403
+ return Fig::EnvironmentVariables::CaseSensitive.new(initial_values)
404
+ end
405
+
406
+ private
407
+
408
+ # path = The local path the file should be downloaded to.
409
+ # cmd = The command to be run on the remote host.
410
+ def ssh_download(user, host, path, cmd)
411
+ return_code = nil
412
+ tempfile = Tempfile.new('tmp')
413
+ Net::SSH.start(host, user) do |ssh|
414
+ ssh.open_channel do |channel|
415
+ channel.exec(cmd)
416
+ channel.on_data() { |ch, data| tempfile << data }
417
+ channel.on_extended_data() { |ch, type, data| Fig::Logging.error "SSH Download ERROR: #{data}" }
418
+ channel.on_request('exit-status') { |ch, request|
419
+ return_code = request.read_long
420
+ }
373
421
  end
422
+ end
374
423
 
375
- return EnvironmentVariables::CaseSensitive.new(initial_values)
424
+ tempfile.close()
425
+
426
+ case return_code
427
+ when NOT_MODIFIED
428
+ tempfile.delete
429
+ return false
430
+ when NOT_FOUND
431
+ tempfile.delete
432
+ raise Fig::NotFoundError.new
433
+ when SUCCESS
434
+ FileUtils.mv(tempfile.path, path)
435
+ return true
436
+ else
437
+ tempfile.delete
438
+ Fig::Logging.fatal "Unable to download file #{path}: #{return_code}"
439
+ raise Fig::NetworkError.new("Unable to download file #{path}: #{return_code}")
376
440
  end
441
+ end
377
442
 
378
- private
379
-
380
- # path = The local path the file should be downloaded to.
381
- # cmd = The command to be run on the remote host.
382
- def ssh_download(user, host, path, cmd)
383
- return_code = nil
384
- tempfile = Tempfile.new('tmp')
385
- Net::SSH.start(host, user) do |ssh|
386
- ssh.open_channel do |channel|
387
- channel.exec(cmd)
388
- channel.on_data() { |ch, data| tempfile << data }
389
- channel.on_extended_data() { |ch, type, data| Logging.error "SSH Download ERROR: #{data}" }
390
- channel.on_request('exit-status') { |ch, request|
391
- return_code = request.read_long
392
- }
393
- end
394
- end
443
+ def ssh_upload(user, host, local_file, remote_file)
444
+ uri = URI.parse(remote_file)
445
+ dir = uri.path[0, uri.path.rindex('/')]
446
+ Net::SSH.start(host, user) do |ssh|
447
+ ssh.exec!("mkdir -p #{dir}")
448
+ end
449
+ Net::SFTP.start(host, user) do |sftp|
450
+ sftp.upload!(local_file, uri.path)
451
+ end
452
+ end
395
453
 
396
- tempfile.close()
397
-
398
- case return_code
399
- when NOT_MODIFIED
400
- tempfile.delete
401
- return false
402
- when NOT_FOUND
403
- tempfile.delete
404
- raise NotFoundError.new
405
- when SUCCESS
406
- FileUtils.mv(tempfile.path, path)
407
- return true
408
- else
409
- tempfile.delete
410
- Logging.fatal "Unable to download file #{path}: #{return_code}"
411
- raise NetworkError.new("Unable to download file #{path}: #{return_code}")
412
- end
454
+ def download_via_http_get(uri_string, file, redirection_limit = 10)
455
+ if redirection_limit < 1
456
+ Fig::Logging.debug 'Too many HTTP redirects.'
457
+ raise Fig::NotFoundError.new
413
458
  end
414
459
 
415
- def ssh_upload(user, host, local_file, remote_file)
416
- uri = URI.parse(remote_file)
417
- dir = uri.path[0, uri.path.rindex('/')]
418
- Net::SSH.start(host, user) do |ssh|
419
- ssh.exec!("mkdir -p #{dir}")
420
- end
421
- Net::SFTP.start(host, user) do |sftp|
422
- sftp.upload!(local_file, uri.path)
423
- end
460
+ response = Net::HTTP.get_response(URI(uri_string))
461
+
462
+ case response
463
+ when Net::HTTPSuccess then
464
+ file.write(response.body)
465
+ when Net::HTTPRedirection then
466
+ location = response['location']
467
+ Fig::Logging.debug "Redirecting to #{location}."
468
+ download_via_http_get(location, file, limit - 1)
469
+ else
470
+ Fig::Logging.debug "Download failed: #{response.code} #{response.message}."
471
+ raise Fig::NotFoundError.new
424
472
  end
425
473
 
426
- private
474
+ return
475
+ end
427
476
 
428
- def log_download(url, path)
429
- Logging.debug "Downloading #{url} to #{path}."
430
- end
477
+ def log_download(url, path)
478
+ Fig::Logging.debug "Downloading #{url} to #{path}."
431
479
  end
432
480
  end