fig 0.1.62 → 0.1.64

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.
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