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.
- data/Changes +156 -0
- data/VERSION +1 -1
- data/bin/fig +9 -2
- data/bin/fig-debug +9 -2
- data/lib/fig/applicationconfiguration.rb +3 -2
- data/lib/fig/atexit.rb +37 -0
- data/lib/fig/backtrace.rb +23 -6
- data/lib/fig/command.rb +131 -31
- data/lib/fig/command/coveragesupport.rb +40 -0
- data/lib/fig/command/listing.rb +8 -8
- data/lib/fig/command/optionerror.rb +8 -0
- data/lib/fig/{options.rb → command/options.rb} +248 -144
- data/lib/fig/command/packageload.rb +161 -62
- data/lib/fig/configfileerror.rb +2 -0
- data/lib/fig/environment.rb +350 -246
- data/lib/fig/environmentvariables/casesensitive.rb +1 -1
- data/lib/fig/figrc.rb +78 -78
- data/lib/fig/grammar.treetop +204 -219
- data/lib/fig/log4rconfigerror.rb +2 -0
- data/lib/fig/operatingsystem.rb +382 -334
- data/lib/fig/package.rb +11 -33
- data/lib/fig/packagecache.rb +1 -1
- data/lib/fig/packagedescriptor.rb +103 -21
- data/lib/fig/packagedescriptorparseerror.rb +16 -0
- data/lib/fig/parser.rb +36 -19
- data/lib/fig/parserpackagebuildstate.rb +56 -0
- data/lib/fig/repository.rb +504 -259
- data/lib/fig/statement.rb +30 -12
- data/lib/fig/statement/archive.rb +8 -5
- data/lib/fig/statement/asset.rb +19 -0
- data/lib/fig/statement/command.rb +2 -2
- data/lib/fig/statement/configuration.rb +20 -20
- data/lib/fig/statement/include.rb +13 -34
- data/lib/fig/statement/override.rb +21 -7
- data/lib/fig/statement/path.rb +22 -2
- data/lib/fig/statement/resource.rb +14 -4
- data/lib/fig/statement/retrieve.rb +34 -4
- data/lib/fig/statement/set.rb +22 -2
- data/lib/fig/workingdirectorymaintainer.rb +197 -0
- data/lib/fig/workingdirectorymetadata.rb +45 -0
- metadata +52 -46
- data/lib/fig/retriever.rb +0 -141
- data/lib/fig/statement/publish.rb +0 -15
data/lib/fig/log4rconfigerror.rb
CHANGED
@@ -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}>
|
data/lib/fig/operatingsystem.rb
CHANGED
@@ -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
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
34
|
+
def get_username()
|
35
|
+
@username ||= ask('Username: ') { |q| q.echo = true }
|
36
|
+
end
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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.
|
49
|
+
ftp.login(get_username, get_password)
|
50
|
+
else
|
51
|
+
ftp.login()
|
52
52
|
end
|
53
|
+
ftp.passive = true
|
54
|
+
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
-
|
56
|
+
def list(dir)
|
57
|
+
Dir.entries(dir) - ['.','..']
|
58
|
+
end
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
def mtime(path)
|
61
|
+
File.mtime(path)
|
62
|
+
end
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
-
|
64
|
+
def write(path, content)
|
65
|
+
File.open(path, 'wb') { |f| f.binmode; f << content }
|
66
|
+
end
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
68
|
+
SUCCESS = 0
|
69
|
+
NOT_MODIFIED = 3
|
70
|
+
NOT_FOUND = 4
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
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
|
-
|
195
|
-
|
196
|
-
|
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
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
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
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
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
|
-
|
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
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
286
|
+
def delete_and_recreate_directory(dir)
|
287
|
+
FileUtils.rm_rf(dir)
|
288
|
+
FileUtils.mkdir_p(dir)
|
289
|
+
end
|
275
290
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
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
|
-
|
295
|
-
|
296
|
-
|
309
|
+
def move_file(dir, from, to)
|
310
|
+
Dir.chdir(dir) { FileUtils.mv(from, to, :force => true) }
|
311
|
+
end
|
297
312
|
|
298
|
-
|
299
|
-
|
300
|
-
|
313
|
+
def log_info(msg)
|
314
|
+
Fig::Logging.info msg
|
315
|
+
end
|
301
316
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
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
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
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
|
-
|
343
|
-
|
344
|
-
|
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
|
-
|
347
|
-
|
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
|
-
|
351
|
-
|
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
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
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
|
-
|
363
|
-
|
364
|
-
|
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
|
-
|
371
|
-
|
372
|
-
|
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
|
-
|
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
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
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
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
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
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
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
|
-
|
474
|
+
return
|
475
|
+
end
|
427
476
|
|
428
|
-
|
429
|
-
|
430
|
-
end
|
477
|
+
def log_download(url, path)
|
478
|
+
Fig::Logging.debug "Downloading #{url} to #{path}."
|
431
479
|
end
|
432
480
|
end
|