fig18 0.1.45-i386-mswin32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,248 @@
1
+ require 'optparse'
2
+ require 'fig/package'
3
+ require 'fig/package/archive'
4
+ require 'fig/package/include'
5
+ require 'fig/package/path'
6
+ require 'fig/package/resource'
7
+ require 'fig/package/set'
8
+
9
+ module Fig
10
+ def parse_descriptor(descriptor)
11
+ # todo should use treetop for these:
12
+ package_name = descriptor =~ /^([^:\/]+)/ ? $1 : nil
13
+ config_name = descriptor =~ /:([^:\/]+)/ ? $1 : nil
14
+ version_name = descriptor =~ /\/([^:\/]+)/ ? $1 : nil
15
+ return package_name, config_name, version_name
16
+ end
17
+
18
+ USAGE = <<EOF
19
+
20
+ Usage: fig [--debug] [--update] [--config <config>] [--get <var> | --list | <package> | -- <command>]
21
+
22
+ Relevant env vars: FIG_REMOTE_URL (required), FIG_HOME (path to local repository cache, defaults
23
+ to $HOME/.fighome).
24
+
25
+ EOF
26
+
27
+ LOG_LEVELS = %w[ off fatal error warn info debug all ]
28
+ LOG_ALIASES = { 'warning' => 'warn' }
29
+
30
+ # Returns hash of option values, the remainders of argv, and an exit code if
31
+ # full program processing occured in this method, otherwise nil.
32
+ def parse_options(argv)
33
+ options = {}
34
+
35
+ parser = OptionParser.new do |opts|
36
+ opts.banner = USAGE
37
+ opts.on('-?', '-h','--help','display this help text') do
38
+ puts opts.help
39
+ puts "\n -- end of fig options; everything following is a command to run in the fig environment.\n\n"
40
+ return nil, nil, 0
41
+ end
42
+
43
+ opts.on('-v', '--version', 'Print fig version') do
44
+ line = nil
45
+
46
+ begin
47
+ File.open(
48
+ "#{File.expand_path(File.dirname(__FILE__) + '/../../VERSION')}"
49
+ ) do |file|
50
+ line = file.gets
51
+ end
52
+ rescue
53
+ $stderr.puts 'Could not retrieve version number. Something has mucked with your gem install.'
54
+ return nil, nil, 1
55
+ end
56
+
57
+ if line !~ /\d+\.\d+\.\d+/
58
+ $stderr.puts %Q<"#{line}" does not look like a version number. Something has mucked with your gem install.>
59
+ return nil, nil, 1
60
+ end
61
+
62
+ puts File.basename($0) + ' v' + line
63
+
64
+ return nil, nil, 0
65
+ end
66
+
67
+ options[:modifiers] = []
68
+ opts.on(
69
+ '-p',
70
+ '--append VAR=VAL',
71
+ 'append (actually, prepend) VAL to environment var VAR, delimited by separator'
72
+ ) do |var_val|
73
+ var, val = var_val.split('=')
74
+ options[:modifiers] << Package::Path.new(var, val)
75
+ end
76
+
77
+ options[:archives] = []
78
+ opts.on(
79
+ '--archive FULLPATH',
80
+ 'include FULLPATH archive in package (when using --publish)'
81
+ ) do |path|
82
+ options[:archives] << Package::Archive.new(path)
83
+ end
84
+
85
+ options[:cleans] = []
86
+ opts.on('--clean PKG', 'remove package from $FIG_HOME') do |descriptor|
87
+ options[:cleans] << descriptor
88
+ end
89
+
90
+ options[:config] = 'default'
91
+ opts.on(
92
+ '-c',
93
+ '--config CFG',
94
+ %q<apply configuration CFG, default is 'default'>
95
+ ) do |config|
96
+ options[:config] = config
97
+ end
98
+
99
+ options[:debug] = false
100
+ opts.on('-d', '--debug', 'print debug info') do
101
+ options[:debug] = true
102
+ end
103
+
104
+ options[:input] = nil
105
+ opts.on(
106
+ '--file FILE',
107
+ %q<read fig file FILE. Use '-' for stdin. See also --no-file>
108
+ ) do |path|
109
+ options[:input] = path
110
+ end
111
+
112
+ options[:force] = nil
113
+ opts.on(
114
+ '--force',
115
+ 'force-overwrite existing version of a package to the remote repo'
116
+ ) do |force|
117
+ options[:force] = force
118
+ end
119
+
120
+ options[:echo] = nil
121
+ opts.on(
122
+ '-g',
123
+ '--get VAR',
124
+ 'print value of environment variable VAR'
125
+ ) do |echo|
126
+ options[:echo] = echo
127
+ end
128
+
129
+ opts.on(
130
+ '-i',
131
+ '--include PKG',
132
+ 'include PKG (with any variable prepends) in environment'
133
+ ) do |descriptor|
134
+ package_name, config_name, version_name = parse_descriptor(descriptor)
135
+ options[:modifiers] << Package::Include.new(package_name, config_name, version_name, {})
136
+ end
137
+
138
+ options[:list] = false
139
+ opts.on('--list', 'list packages in $FIG_HOME') do
140
+ options[:list] = true
141
+ end
142
+
143
+ options[:list_configs] = []
144
+ opts.on(
145
+ '--list-configs PKG', 'list configurations in package'
146
+ ) do |descriptor|
147
+ options[:list_configs] << descriptor
148
+ end
149
+
150
+ options[:list_remote] = false
151
+ opts.on('--list-remote', 'list packages in remote repo') do
152
+ options[:list_remote] = true
153
+ end
154
+
155
+ options[:login] = false
156
+ opts.on(
157
+ '-l', '--login', 'login to remote repo as a non-anonymous user'
158
+ ) do
159
+ options[:login] = true
160
+ end
161
+
162
+ opts.on(
163
+ '--no-file', 'ignore package.fig file in current directory'
164
+ ) do |path|
165
+ options[:input] = :none
166
+ end
167
+
168
+ options[:publish] = nil
169
+ opts.on(
170
+ '--publish PKG', 'install PKG in $FIG_HOME and in remote repo'
171
+ ) do |publish|
172
+ options[:publish] = publish
173
+ end
174
+
175
+ options[:publish_local] = nil
176
+ opts.on(
177
+ '--publish-local PKG', 'install package only in $FIG_HOME'
178
+ ) do |publish_local|
179
+ options[:publish_local] = publish_local
180
+ end
181
+
182
+ options[:resources] =[]
183
+ opts.on(
184
+ '--resource FULLPATH',
185
+ 'include FULLPATH resource in package (when using --publish)'
186
+ ) do |path|
187
+ options[:resources] << Package::Resource.new(path)
188
+ end
189
+
190
+ opts.on(
191
+ '-s', '--set VAR=VAL', 'set environment variable VAR to VAL'
192
+ ) do |var_val|
193
+ var, val = var_val.split('=')
194
+ options[:modifiers] << Package::Set.new(var, val)
195
+ end
196
+
197
+ options[:update] = false
198
+ opts.on(
199
+ '-u',
200
+ '--update',
201
+ 'check remote repo for updates and download to $FIG_HOME as necessary'
202
+ ) do
203
+ options[:update] = true; options[:retrieve] = true
204
+ end
205
+
206
+ options[:update_if_missing] = false
207
+ opts.on(
208
+ '-m',
209
+ '--update-if-missing',
210
+ 'check remote repo for updates only if package missing from $FIG_HOME'
211
+ ) do
212
+ options[:update_if_missing] = true; options[:retrieve] = true
213
+ end
214
+
215
+ opts.on(
216
+ '--figrc PATH', 'add PATH to configuration used for Fig'
217
+ ) do |path|
218
+ options[:figrc] = path
219
+ end
220
+
221
+ opts.on('--no-figrc', 'ignore ~/.figrc') { options[:no_figrc] = true }
222
+
223
+ opts.on(
224
+ '--log-config PATH', 'use PATH file as configuration for Log4r'
225
+ ) do |path|
226
+ options[:log_config] = path
227
+ end
228
+
229
+ level_list = LOG_LEVELS.join(', ')
230
+ opts.on(
231
+ '--log-level LEVEL',
232
+ LOG_LEVELS,
233
+ LOG_ALIASES,
234
+ 'set logging level to LEVEL',
235
+ " (#{level_list})"
236
+ ) do |log_level|
237
+ options[:log_level] = log_level
238
+ end
239
+
240
+ options[:home] = ENV['FIG_HOME'] || File.expand_path('~/.fighome')
241
+ end
242
+
243
+ # Need to catch the exception thrown from parser and retranslate into a fig exception
244
+ parser.parse!(argv)
245
+
246
+ return options, argv, nil
247
+ end
248
+ end
data/lib/fig/os.rb ADDED
@@ -0,0 +1,416 @@
1
+ require 'fileutils'
2
+ # Must specify absolute path of ::Archive when using
3
+ # this module to avoid conflicts with Fig::Package::Archive
4
+ require 'libarchive_ruby' unless RUBY_PLATFORM == 'java'
5
+ require 'uri'
6
+ require 'net/http'
7
+ require 'net/ssh'
8
+ require 'net/sftp'
9
+ require 'net/netrc'
10
+ require 'tempfile'
11
+ require 'highline/import'
12
+
13
+ require 'fig/logging'
14
+ require 'fig/networkerror'
15
+ require 'fig/notfounderror'
16
+
17
+ module Fig
18
+ class OS
19
+ def initialize(login)
20
+ @login = login
21
+ @username = ENV['FIG_USERNAME']
22
+ @password = ENV['FIG_PASSWORD']
23
+ end
24
+
25
+ def get_username()
26
+ @username ||= ask('Username: ') { |q| q.echo = true }
27
+ end
28
+
29
+ def get_password()
30
+ @password ||= ask('Password: ') { |q| q.echo = false }
31
+ end
32
+
33
+ def ftp_login(ftp, host)
34
+ if @login
35
+ rc = Net::Netrc.locate(host)
36
+ if rc
37
+ @username = rc.login
38
+ @password = rc.password
39
+ end
40
+ ftp.login(get_username, get_password)
41
+ else
42
+ ftp.login()
43
+ end
44
+ ftp.passive = true
45
+ end
46
+
47
+ def list(dir)
48
+ Dir.entries(dir) - ['.','..']
49
+ end
50
+
51
+ def exist?(path)
52
+ File.exist?(path)
53
+ end
54
+
55
+ def mtime(path)
56
+ File.mtime(path)
57
+ end
58
+
59
+ def read(path)
60
+ File.read(path)
61
+ end
62
+
63
+ def write(path, content)
64
+ File.open(path, 'wb') { |f| f.binmode; f << content }
65
+ end
66
+
67
+ SUCCESS = 0
68
+ NOT_MODIFIED = 3
69
+ NOT_FOUND = 4
70
+
71
+ def strip_paths_for_list(ls_output, packages, path)
72
+ if not ls_output.nil?
73
+ ls_output = ls_output.gsub(path + '/', '').gsub(path, '').split("\n")
74
+ ls_output.each do |line|
75
+ parts = line.gsub(/\\/, '/').sub(/^\.\//, '').sub(/:$/, '').chomp().split('/')
76
+ packages << parts.join('/') if parts.size == 2
77
+ end
78
+ end
79
+ end
80
+
81
+ def download_list(url)
82
+ begin
83
+ uri = URI.parse(url)
84
+ rescue
85
+ Logging.fatal %Q<Unable to parse url: "#{url}">
86
+ raise NetworkError.new(%Q<Unable to parse url: "#{url}">)
87
+ end
88
+ case uri.scheme
89
+ when 'ftp'
90
+ ftp = Net::FTP.new(uri.host)
91
+ ftp_login(ftp, uri.host)
92
+ ftp.chdir(uri.path)
93
+ dirs = ftp.nlst
94
+ ftp.close
95
+
96
+ download_ftp_list(uri, dirs)
97
+ when 'ssh'
98
+ packages = []
99
+ Net::SSH.start(uri.host, uri.user) do |ssh|
100
+ ls = ssh.exec!("[ -d #{uri.path} ] && find #{uri.path}")
101
+ strip_paths_for_list(ls, packages, uri.path)
102
+ end
103
+ packages
104
+ when 'file'
105
+ packages = []
106
+ ls = %x<[ -d #{uri.path} ] && find #{uri.path}>
107
+ strip_paths_for_list(ls, packages, uri.path)
108
+ return packages
109
+ else
110
+ Logging.fatal "Protocol not supported: #{url}"
111
+ raise NetworkError.new("Protocol not supported: #{url}")
112
+ end
113
+ end
114
+
115
+ def download_ftp_list(uri, dirs)
116
+ # Run a bunch of these in parallel since they're slow as hell
117
+ num_threads = (ENV['FIG_FTP_THREADS'] || '16').to_i
118
+ threads = []
119
+ all_packages = []
120
+ (0..num_threads-1).each { |num| all_packages[num] = [] }
121
+ (0..num_threads-1).each do |num|
122
+ threads << Thread.new do
123
+ packages = all_packages[num]
124
+ ftp = Net::FTP.new(uri.host)
125
+ ftp_login(ftp, uri.host)
126
+ ftp.chdir(uri.path)
127
+ pos = num
128
+ while pos < dirs.length
129
+ pkg = dirs[pos]
130
+ begin
131
+ ftp.nlst(dirs[pos]).each do |ver|
132
+ packages << pkg + '/' + ver
133
+ end
134
+ rescue Net::FTPPermError
135
+ # ignore
136
+ end
137
+ pos += num_threads
138
+ end
139
+ ftp.close
140
+ end
141
+ end
142
+ threads.each { |thread| thread.join }
143
+ all_packages.flatten.sort
144
+ end
145
+
146
+ def download(url, path)
147
+ FileUtils.mkdir_p(File.dirname(path))
148
+ uri = URI.parse(url)
149
+ case uri.scheme
150
+ when 'ftp'
151
+ ftp = Net::FTP.new(uri.host)
152
+ ftp_login(ftp, uri.host)
153
+ begin
154
+ if File.exist?(path) && ftp.mtime(uri.path) <= File.mtime(path)
155
+ Logging.debug "#{path} is up to date."
156
+ return false
157
+ else
158
+ log_download(url, path)
159
+ ftp.getbinaryfile(uri.path, path, 256*1024)
160
+ return true
161
+ end
162
+ rescue Net::FTPPermError => e
163
+ Logging.warn e
164
+ raise NotFoundError.new
165
+ end
166
+ when 'http'
167
+ http = Net::HTTP.new(uri.host)
168
+ log_download(url, path)
169
+ File.open(path, 'wb') do |file|
170
+ file.binmode
171
+ http.get(uri.path) do |block|
172
+ file.write(block)
173
+ end
174
+ end
175
+ when 'ssh'
176
+ # TODO need better way to do conditional download
177
+ timestamp = File.exist?(path) ? File.mtime(path).to_i : 0
178
+ # Requires that remote installation of fig be at the same location as the local machine.
179
+ cmd = `which fig-download`.strip + " #{timestamp} #{uri.path}"
180
+ log_download(url, path)
181
+ ssh_download(uri.user, uri.host, path, cmd)
182
+ when 'file'
183
+ begin
184
+ FileUtils.cp(uri.path, path)
185
+ rescue Errno::ENOENT => e
186
+ raise NotFoundError.new
187
+ end
188
+ else
189
+ Logging.fatal "Unknown protocol: #{url}"
190
+ raise NetworkError.new("Unknown protocol: #{url}")
191
+ end
192
+ end
193
+
194
+ def download_resource(url, dir)
195
+ FileUtils.mkdir_p(dir)
196
+ download(url, File.join(dir, URI.parse(url).path.split('/').last))
197
+ end
198
+
199
+ def download_archive(url, dir)
200
+ FileUtils.mkdir_p(dir)
201
+ basename = URI.parse(url).path.split('/').last
202
+ path = File.join(dir, basename)
203
+ download(url, path)
204
+ case basename
205
+ when /\.tar\.gz$/
206
+ unpack_archive(dir, path)
207
+ when /\.tgz$/
208
+ unpack_archive(dir, path)
209
+ when /\.tar\.bz2$/
210
+ unpack_archive(dir, path)
211
+ when /\.zip$/
212
+ unpack_archive(dir, path)
213
+ else
214
+ Logging.fatal "Unknown archive type: #{basename}"
215
+ raise NetworkError.new("Unknown archive type: #{basename}")
216
+ end
217
+ end
218
+
219
+ def upload(local_file, remote_file, user)
220
+ Logging.debug "Uploading #{local_file} to #{remote_file}."
221
+ uri = URI.parse(remote_file)
222
+ case uri.scheme
223
+ when 'ssh'
224
+ ssh_upload(uri.user, uri.host, local_file, remote_file)
225
+ when 'ftp'
226
+ # fail unless system "curl -T #{local_file} --create-dirs --ftp-create-dirs #{remote_file}"
227
+ require 'net/ftp'
228
+ ftp_uri = URI.parse(ENV['FIG_REMOTE_URL'])
229
+ ftp_root_path = ftp_uri.path
230
+ ftp_root_dirs = ftp_uri.path.split('/')
231
+ remote_publish_path = uri.path[0, uri.path.rindex('/')]
232
+ remote_publish_dirs = remote_publish_path.split('/')
233
+ # Use array subtraction to deduce which project/version folder to upload to,
234
+ # i.e. [1,2,3] - [2,3,4] = [1]
235
+ remote_project_dirs = remote_publish_dirs - ftp_root_dirs
236
+ Net::FTP.open(uri.host) do |ftp|
237
+ ftp_login(ftp, uri.host)
238
+ # Assume that the FIG_REMOTE_URL path exists.
239
+ ftp.chdir(ftp_root_path)
240
+ remote_project_dirs.each do |dir|
241
+ # Can't automatically create parent directories, so do it manually.
242
+ if ftp.nlst().index(dir).nil?
243
+ ftp.mkdir(dir)
244
+ ftp.chdir(dir)
245
+ else
246
+ ftp.chdir(dir)
247
+ end
248
+ end
249
+ ftp.putbinaryfile(local_file)
250
+ end
251
+ when 'file'
252
+ FileUtils.mkdir_p(File.dirname(uri.path))
253
+ FileUtils.cp(local_file, uri.path)
254
+ else
255
+ Logging.fatal "Unknown protocol: #{uri}"
256
+ raise NetworkError.new("Unknown protocol: #{uri}")
257
+ end
258
+ end
259
+
260
+ def clear_directory(dir)
261
+ FileUtils.rm_rf(dir)
262
+ FileUtils.mkdir_p(dir)
263
+ end
264
+
265
+ def copy(source, target, msg = nil)
266
+ if File.directory?(source)
267
+ FileUtils.mkdir_p(target)
268
+ Dir.foreach(source) do |child|
269
+ if child != '.' and child != '..'
270
+ copy(File.join(source, child), File.join(target, child), msg)
271
+ end
272
+ end
273
+ else
274
+ if !File.exist?(target) || File.mtime(source) != File.mtime(target)
275
+ log_info "#{msg} #{target}" if msg
276
+ FileUtils.mkdir_p(File.dirname(target))
277
+ FileUtils.cp(source, target)
278
+ File.utime(File.atime(source), File.mtime(source), target)
279
+ end
280
+ end
281
+ end
282
+
283
+ def move_file(dir, from, to)
284
+ Dir.chdir(dir) { FileUtils.mv(from, to, :force => true) }
285
+ end
286
+
287
+ def log_info(msg)
288
+ Logging.info msg
289
+ end
290
+
291
+ # Expects files_to_archive as an Array of filenames.
292
+ def create_archive(archive_name, files_to_archive)
293
+ if OS.java?
294
+ `tar czvf #{archive_name} #{files_to_archive.join(' ')}`
295
+ else
296
+ # TODO: Need to verify files_to_archive exists.
297
+ ::Archive.write_open_filename(archive_name, ::Archive::COMPRESSION_GZIP, ::Archive::FORMAT_TAR) do |ar|
298
+ files_to_archive.each do |fn|
299
+ ar.new_entry do |entry|
300
+ entry.copy_stat(fn)
301
+ entry.pathname = fn
302
+ ar.write_header(entry)
303
+ if !entry.directory?
304
+ ar.write_data(open(fn) {|f| f.binmode; f.read })
305
+ end
306
+ end
307
+ end
308
+ end
309
+ end
310
+ end
311
+
312
+ # This method can handle the following archive types:
313
+ # .tar.bz2
314
+ # .tar.gz
315
+ # .tgz
316
+ # .zip
317
+ def unpack_archive(dir, file)
318
+ Dir.chdir(dir) do
319
+ if OS.java?
320
+ `tar xzvf #{file}`
321
+ else
322
+ ::Archive.read_open_filename(file) do |ar|
323
+ while entry = ar.next_header
324
+ ar.extract(entry)
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
330
+
331
+ def self.windows?
332
+ Config::CONFIG['host_os'] =~ /mswin|mingw/
333
+ end
334
+
335
+ def self.java?
336
+ RUBY_PLATFORM == 'java'
337
+ end
338
+
339
+ def self.unix?
340
+ !windows?
341
+ end
342
+
343
+ def shell_exec(cmd)
344
+ if OS.windows?
345
+ Windows.shell_exec_windows(cmd)
346
+ else
347
+ shell_exec_unix(cmd)
348
+ end
349
+ end
350
+
351
+ private
352
+
353
+ def shell_exec_unix(cmd)
354
+ Kernel.exec(ENV['SHELL'], '-c', cmd.join(' '))
355
+ end
356
+
357
+ def shell_exec_windows(cmd)
358
+ #command = ['C:/WINDOWS/system32/cmd.exe', '/C', 'call'] + cmd
359
+ command = ['cmd.exe', '/C'] + cmd
360
+ command = command.join(' ')
361
+ Kernel.exec(command)
362
+ end
363
+
364
+ # path = The local path the file should be downloaded to.
365
+ # cmd = The command to be run on the remote host.
366
+ def ssh_download(user, host, path, cmd)
367
+ return_code = nil
368
+ tempfile = Tempfile.new('tmp')
369
+ Net::SSH.start(host, user) do |ssh|
370
+ ssh.open_channel do |channel|
371
+ channel.exec(cmd)
372
+ channel.on_data() { |ch, data| tempfile << data }
373
+ channel.on_extended_data() { |ch, type, data| Logging.error "SSH Download ERROR: #{data}" }
374
+ channel.on_request('exit-status') { |ch, request|
375
+ return_code = request.read_long
376
+ }
377
+ end
378
+ end
379
+
380
+ tempfile.close()
381
+
382
+ case return_code
383
+ when NOT_MODIFIED
384
+ tempfile.delete
385
+ return false
386
+ when NOT_FOUND
387
+ tempfile.delete
388
+ raise NotFoundError.new
389
+ when SUCCESS
390
+ FileUtils.mv(tempfile.path, path)
391
+ return true
392
+ else
393
+ tempfile.delete
394
+ Logging.fatal "Unable to download file #{path}: #{return_code}"
395
+ raise NetworkError.new("Unable to download file #{path}: #{return_code}")
396
+ end
397
+ end
398
+
399
+ def ssh_upload(user, host, local_file, remote_file)
400
+ uri = URI.parse(remote_file)
401
+ dir = uri.path[0, uri.path.rindex('/')]
402
+ Net::SSH.start(host, user) do |ssh|
403
+ ssh.exec!("mkdir -p #{dir}")
404
+ end
405
+ Net::SFTP.start(host, user) do |sftp|
406
+ sftp.upload!(local_file, uri.path)
407
+ end
408
+ end
409
+
410
+ private
411
+
412
+ def log_download(url, path)
413
+ Logging.debug "Downloading #{url} to #{path}."
414
+ end
415
+ end
416
+ end
@@ -0,0 +1,24 @@
1
+ require 'fig/logging'
2
+ require 'fig/packageerror'
3
+ require 'fig/package/statement'
4
+
5
+ module Fig; end
6
+ class Fig::Package; end
7
+
8
+ class Fig::Package::Archive
9
+ include Fig::Package::Statement
10
+
11
+ attr_reader :url
12
+
13
+ def initialize(url)
14
+ @url = url
15
+ end
16
+
17
+ def urls
18
+ return [@url]
19
+ end
20
+
21
+ def unparse(indent)
22
+ %Q<#{indent}archive "#{url}">
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ require 'fig/logging'
2
+ require 'fig/packageerror'
3
+ require 'fig/package/statement'
4
+
5
+ module Fig; end
6
+ class Fig::Package; end
7
+
8
+ class Fig::Package::Command
9
+ include Fig::Package::Statement
10
+
11
+ attr_reader :command
12
+
13
+ def initialize(command)
14
+ @command = command
15
+ end
16
+
17
+ def unparse(indent)
18
+ %Q<#{indent}command "#{@command}">
19
+ end
20
+ end