fig18 0.1.39 → 0.1.40

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/lib/fig/options.rb CHANGED
@@ -6,101 +6,238 @@ module Fig
6
6
  # todo should use treetop for these:
7
7
  package_name = descriptor =~ /^([^:\/]+)/ ? $1 : nil
8
8
  config_name = descriptor =~ /:([^:\/]+)/ ? $1 : nil
9
- version_name = descriptor =~ /\/([^:\/]+)/ ? $1 : nil
9
+ version_name = descriptor =~ /\/([^:\/]+)/ ? $1 : nil
10
10
  return package_name, config_name, version_name
11
11
  end
12
12
 
13
+ USAGE = <<EOF
14
+
15
+ Usage: fig [--debug] [--update] [--config <config>] [--get <var> | --list | <package> | -- <command>]
16
+
17
+ Relevant env vars: FIG_REMOTE_URL (required), FIG_HOME (path to local repository cache, defaults
18
+ to $HOME/.fighome).
19
+
20
+ EOF
21
+
22
+ LOG_LEVELS = %w[ off fatal error warn info debug all ]
23
+ LOG_ALIASES = { 'warning' => 'warn' }
24
+
25
+ # Returns hash of option values, the remainders of argv, and an exit code if
26
+ # full program processing occured in this method, otherwise nil.
13
27
  def parse_options(argv)
14
28
  options = {}
15
29
 
16
30
  parser = OptionParser.new do |opts|
17
- opts.banner = "Usage: fig [--debug] [--update] [--config <config>] [--get <var> | --list | <package> | - <command>]"
18
-
31
+ opts.banner = USAGE
19
32
  opts.on('-?', '-h','--help','display this help text') do
20
- puts opts
21
- exit 1
22
- end
33
+ puts opts.help
34
+ puts "\n -- end of fig options; everything following is a command to run in the fig environment.\n\n"
35
+ return nil, nil, 0
36
+ end
23
37
 
24
- options[:debug] = false
25
- opts.on('-d', '--debug', 'print debug info') { options[:debug] = true }
38
+ opts.on('-v', '--version', 'Print fig version') do
39
+ line = nil
26
40
 
27
- options[:update] = false
28
- opts.on('-u', '--update', 'check remote repository for updates') { options[:update] = true; options[:retrieve] = true }
41
+ begin
42
+ File.open(
43
+ "#{File.expand_path(File.dirname(__FILE__) + '/../../VERSION')}"
44
+ ) do |file|
45
+ line = file.gets
46
+ end
47
+ rescue
48
+ $stderr.puts 'Could not retrieve version number. Something has mucked with your gem install.'
49
+ return nil, nil, 1
50
+ end
29
51
 
30
- options[:update_if_missing] = false
31
- opts.on('-m', '--update-if-missing', 'check for updates only if package is missing locally') { options[:update_if_missing] = true; options[:retrieve] = true }
52
+ if line !~ /\d+\.\d+\.\d+/
53
+ $stderr.puts %Q<"#{line}" does not look like a version number. Something has mucked with your gem install.>
54
+ return nil, nil, 1
55
+ end
32
56
 
33
- options[:config] = "default"
34
- opts.on('-c', '--config CFG', 'name of configuration to apply') { |config| options[:config] = config }
57
+ puts File.basename($0) + ' v' + line
35
58
 
36
- options[:echo] = nil
37
- opts.on('-g', '--get VAR', 'print value of environment variable') { |echo| options[:echo] = echo }
59
+ return nil, nil, 0
60
+ end
38
61
 
39
- options[:publish] = nil
40
- opts.on('--publish PKG', 'install package in local and remote repositories') { |publish| options[:publish] = publish }
62
+ options[:modifiers] = []
63
+ opts.on(
64
+ '-p',
65
+ '--append VAR=VAL',
66
+ 'append (actually, prepend) VAL to environment var VAR, delimited by separator'
67
+ ) do |var_val|
68
+ var, val = var_val.split('=')
69
+ options[:modifiers] << Path.new(var, val)
70
+ end
41
71
 
42
- options[:publish_local] = nil
43
- opts.on('--publish-local PKG', 'install package in local repositorie only') { |publish_local| options[:publish_local] = publish_local }
72
+ options[:archives] = []
73
+ opts.on(
74
+ '--archive FULLPATH',
75
+ 'include FULLPATH archive in package (when using --publish)'
76
+ ) do |path|
77
+ options[:archives] << Archive.new(path)
78
+ end
44
79
 
45
- options[:force] = nil
46
- opts.on('--force', 'force overwriting of an existing remote package version') { |force| options[:force] = force }
80
+ options[:cleans] = []
81
+ opts.on('--clean PKG', 'remove package from $FIG_HOME') do |descriptor|
82
+ options[:cleans] << descriptor
83
+ end
47
84
 
48
- options[:resources] =[]
49
- opts.on('--resource PATH', 'resource to include in package (when using --publish)') do |path|
50
- options[:resources] << Resource.new(path)
85
+ options[:config] = 'default'
86
+ opts.on(
87
+ '-c',
88
+ '--config CFG',
89
+ %q<apply configuration CFG, default is 'default'>
90
+ ) do |config|
91
+ options[:config] = config
51
92
  end
52
93
 
53
- options[:archives] =[]
54
- opts.on('--archive PATH', 'archive to include in package (when using --publish)') do |path|
55
- options[:archives] << Archive.new(path)
94
+ options[:debug] = false
95
+ opts.on('-d', '--debug', 'print debug info') do
96
+ options[:debug] = true
56
97
  end
57
98
 
58
- options[:login] = false
59
- opts.on('-l', '--login', 'login into the FTP server as a non-anonymous user') { options[:login] = true }
99
+ options[:input] = nil
100
+ opts.on(
101
+ '--file FILE',
102
+ %q<read fig file FILE. Use '-' for stdin. See also --no-file>
103
+ ) do |path|
104
+ options[:input] = path
105
+ end
106
+
107
+ options[:force] = nil
108
+ opts.on(
109
+ '--force',
110
+ 'force-overwrite existing version of a package to the remote repo'
111
+ ) do |force|
112
+ options[:force] = force
113
+ end
114
+
115
+ options[:echo] = nil
116
+ opts.on(
117
+ '-g',
118
+ '--get VAR',
119
+ 'print value of environment variable VAR'
120
+ ) do |echo|
121
+ options[:echo] = echo
122
+ end
123
+
124
+ opts.on(
125
+ '-i',
126
+ '--include PKG',
127
+ 'include PKG (with any variable prepends) in environment'
128
+ ) do |descriptor|
129
+ package_name, config_name, version_name = parse_descriptor(descriptor)
130
+ options[:modifiers] << Include.new(package_name, config_name, version_name, {})
131
+ end
60
132
 
61
133
  options[:list] = false
62
- opts.on('--list', 'list packages in local repository') { options[:list] = true }
134
+ opts.on('--list', 'list packages in $FIG_HOME') do
135
+ options[:list] = true
136
+ end
137
+
138
+ options[:list_configs] = []
139
+ opts.on(
140
+ '--list-configs PKG', 'list configurations in package'
141
+ ) do |descriptor|
142
+ options[:list_configs] << descriptor
143
+ end
63
144
 
64
145
  options[:list_remote] = false
65
- opts.on('--list-remote', 'list packages in remote repository') { options[:list_remote] = true }
146
+ opts.on('--list-remote', 'list packages in remote repo') do
147
+ options[:list_remote] = true
148
+ end
66
149
 
67
- options[:list_configs] = []
68
- opts.on('--list-configs PKG', 'list configurations in package') { |descriptor| options[:list_configs] << descriptor }
150
+ options[:login] = false
151
+ opts.on(
152
+ '-l', '--login', 'login to remote repo as a non-anonymous user'
153
+ ) do
154
+ options[:login] = true
155
+ end
69
156
 
70
- options[:cleans] = []
71
- opts.on('--clean PKG', 'remove package from local repository') { |descriptor| options[:cleans] << descriptor }
157
+ opts.on(
158
+ '--no-file', 'ignore package.fig file in current directory'
159
+ ) do |path|
160
+ options[:input] = :none
161
+ end
72
162
 
73
- options[:modifiers] = []
163
+ options[:publish] = nil
164
+ opts.on(
165
+ '--publish PKG', 'install PKG in $FIG_HOME and in remote repo'
166
+ ) do |publish|
167
+ options[:publish] = publish
168
+ end
74
169
 
75
- opts.on('-i', '--include PKG', 'include package in environment') do |descriptor|
76
- package_name, config_name, version_name = parse_descriptor(descriptor)
77
- options[:modifiers] << Include.new(package_name, config_name, version_name, {})
170
+ options[:publish_local] = nil
171
+ opts.on(
172
+ '--publish-local PKG', 'install package only in $FIG_HOME'
173
+ ) do |publish_local|
174
+ options[:publish_local] = publish_local
78
175
  end
79
176
 
80
- # opts.on('-o', '--override PKG', 'override version of included package') do |descriptor|
81
- # package_name, config_name, version_name = parse_descriptor(descriptor)
82
- # options[:modifiers] << Include.new(package_name, config_name, version_name, {})
83
- # end
177
+ options[:resources] =[]
178
+ opts.on(
179
+ '--resource FULLPATH',
180
+ 'include FULLPATH resource in package (when using --publish)'
181
+ ) do |path|
182
+ options[:resources] << Resource.new(path)
183
+ end
84
184
 
85
- opts.on('-s', '--set VAR=VAL', 'set environment variable') do |var_val|
185
+ opts.on(
186
+ '-s', '--set VAR=VAL', 'set environment variable VAR to VAL'
187
+ ) do |var_val|
86
188
  var, val = var_val.split('=')
87
- options[:modifiers] << Set.new(var, val)
189
+ options[:modifiers] << Set.new(var, val)
88
190
  end
89
191
 
90
- opts.on('-p', '--append VAR=VAL', 'append environment variable') do |var_val|
91
- var, val = var_val.split('=')
92
- options[:modifiers] << Path.new(var, val)
192
+ options[:update] = false
193
+ opts.on(
194
+ '-u',
195
+ '--update',
196
+ 'check remote repo for updates and download to $FIG_HOME as necessary'
197
+ ) do
198
+ options[:update] = true; options[:retrieve] = true
93
199
  end
94
200
 
95
- options[:input] = nil
96
- opts.on('--file FILE', 'fig file to read (use - for stdin)') { |path| options[:input] = path }
97
- opts.on('--no-file', 'ignore package.fig file in current directory') { |path| options[:input] = :none }
201
+ options[:update_if_missing] = false
202
+ opts.on(
203
+ '-m',
204
+ '--update-if-missing',
205
+ 'check remote repo for updates only if package missing from $FIG_HOME'
206
+ ) do
207
+ options[:update_if_missing] = true; options[:retrieve] = true
208
+ end
209
+
210
+ opts.on(
211
+ '--figrc PATH', 'add PATH to configuration used for Fig'
212
+ ) do |path|
213
+ options[:figrc] = path
214
+ end
215
+
216
+ opts.on('--no-figrc', 'ignore ~/.figrc') { options[:no_figrc] = true }
217
+
218
+ opts.on(
219
+ '--log-config PATH', 'use PATH file as configuration for Log4r'
220
+ ) do |path|
221
+ options[:log_config] = path
222
+ end
223
+
224
+ level_list = LOG_LEVELS.join(', ')
225
+ opts.on(
226
+ '--log-level LEVEL',
227
+ LOG_LEVELS,
228
+ LOG_ALIASES,
229
+ 'set logging level to LEVEL',
230
+ " (#{level_list})"
231
+ ) do |log_level|
232
+ options[:log_level] = log_level
233
+ end
98
234
 
99
- options[:home] = ENV['FIG_HOME'] || File.expand_path("~/.fighome")
235
+ options[:home] = ENV['FIG_HOME'] || File.expand_path('~/.fighome')
100
236
  end
101
237
 
238
+ # Need to catch the exception thrown from parser and retranslate into a fig exception
102
239
  parser.parse!(argv)
103
240
 
104
- return options, argv
241
+ return options, argv, nil
105
242
  end
106
243
  end
data/lib/fig/os.rb CHANGED
@@ -10,23 +10,24 @@ require 'net/netrc'
10
10
  require 'tempfile'
11
11
  require 'highline/import'
12
12
 
13
- module Fig
14
- class NotFoundException < Exception
15
- end
13
+ require 'fig/logging'
14
+ require 'fig/networkerror'
15
+ require 'fig/notfounderror'
16
16
 
17
+ module Fig
17
18
  class OS
18
19
  def initialize(login)
19
20
  @login = login
20
- @username = ENV["FIG_USERNAME"]
21
- @password = ENV["FIG_PASSWORD"]
21
+ @username = ENV['FIG_USERNAME']
22
+ @password = ENV['FIG_PASSWORD']
22
23
  end
23
24
 
24
25
  def get_username()
25
- @username ||= ask("Username: ") { |q| q.echo = true }
26
+ @username ||= ask('Username: ') { |q| q.echo = true }
26
27
  end
27
-
28
+
28
29
  def get_password()
29
- @password ||= ask("Password: ") { |q| q.echo = false }
30
+ @password ||= ask('Password: ') { |q| q.echo = false }
30
31
  end
31
32
 
32
33
  def ftp_login(ftp, host)
@@ -37,7 +38,7 @@ module Fig
37
38
  @password = rc.password
38
39
  end
39
40
  ftp.login(get_username, get_password)
40
- else
41
+ else
41
42
  ftp.login()
42
43
  end
43
44
  ftp.passive = true
@@ -46,36 +47,36 @@ module Fig
46
47
  def list(dir)
47
48
  Dir.entries(dir) - ['.','..']
48
49
  end
49
-
50
+
50
51
  def exist?(path)
51
52
  File.exist?(path)
52
53
  end
53
-
54
+
54
55
  def mtime(path)
55
56
  File.mtime(path)
56
57
  end
57
-
58
+
58
59
  def read(path)
59
60
  File.read(path)
60
61
  end
61
-
62
+
62
63
  def write(path, content)
63
- File.open(path, "wb") { |f| f.binmode; f << content }
64
+ File.open(path, 'wb') { |f| f.binmode; f << content }
64
65
  end
65
-
66
+
66
67
  SUCCESS = 0
67
68
  NOT_MODIFIED = 3
68
69
  NOT_FOUND = 4
69
-
70
+
70
71
  def download_list(url)
71
72
  begin
72
73
  uri = URI.parse(url)
73
- rescue
74
- $stderr.puts "Unable to parse url: '#{url}'"
75
- exit 10
74
+ rescue
75
+ Logging.fatal %Q<Unable to parse url: "#{url}">
76
+ raise NetworkError.new
76
77
  end
77
78
  case uri.scheme
78
- when "ftp"
79
+ when 'ftp'
79
80
  ftp = Net::FTP.new(uri.host)
80
81
  ftp_login(ftp, uri.host)
81
82
  ftp.chdir(uri.path)
@@ -83,12 +84,12 @@ module Fig
83
84
  ftp.close
84
85
 
85
86
  download_ftp_list(uri, dirs)
86
- when "ssh"
87
+ when 'ssh'
87
88
  packages = []
88
89
  Net::SSH.start(uri.host, uri.user) do |ssh|
89
90
  ls = ssh.exec!("[ -d #{uri.path} ] && find #{uri.path}")
90
91
  if not ls.nil?
91
- ls = ls.gsub(uri.path + "/", "").gsub(uri.path, "").split("\n")
92
+ ls = ls.gsub(uri.path + '/', '').gsub(uri.path, '').split("\n")
92
93
  ls.each do |line|
93
94
  parts = line.gsub(/\\/, '/').sub(/^\.\//, '').sub(/:$/, '').chomp().split('/')
94
95
  packages << parts.join('/') if parts.size == 2
@@ -97,16 +98,16 @@ module Fig
97
98
  end
98
99
  packages
99
100
  else
100
- $stderr.puts "Protocol not supported: #{url}"
101
- exit 10
101
+ Logging.fatal "Protocol not supported: #{url}"
102
+ raise NetworkError.new
102
103
  end
103
104
  end
104
105
 
105
106
  def download_ftp_list(uri, dirs)
106
107
  # Run a bunch of these in parallel since they're slow as hell
107
- num_threads = (ENV["FIG_FTP_THREADS"] || "16").to_i
108
+ num_threads = (ENV['FIG_FTP_THREADS'] || '16').to_i
108
109
  threads = []
109
- all_packages = []
110
+ all_packages = []
110
111
  (0..num_threads-1).each { |num| all_packages[num] = [] }
111
112
  (0..num_threads-1).each do |num|
112
113
  threads << Thread.new do
@@ -137,46 +138,49 @@ module Fig
137
138
  FileUtils.mkdir_p(File.dirname(path))
138
139
  uri = URI.parse(url)
139
140
  case uri.scheme
140
- when "ftp"
141
+ when 'ftp'
141
142
  ftp = Net::FTP.new(uri.host)
142
143
  ftp_login(ftp, uri.host)
143
144
  begin
144
145
  if File.exist?(path) && ftp.mtime(uri.path) <= File.mtime(path)
146
+ Logging.debug "#{path} is up to date."
145
147
  return false
146
- else
147
- $stderr.puts "downloading #{url}"
148
+ else
149
+ log_download(url, path)
148
150
  ftp.getbinaryfile(uri.path, path, 256*1024)
149
151
  return true
150
152
  end
151
- rescue Net::FTPPermError
152
- raise NotFoundException.new
153
+ rescue Net::FTPPermError => e
154
+ Logging.warn e
155
+ raise NotFoundError.new
153
156
  end
154
- when "http"
157
+ when 'http'
155
158
  http = Net::HTTP.new(uri.host)
156
- $stderr.puts "downloading #{url}"
157
- File.open(path, "wb") do |file|
159
+ log_download(url, path)
160
+ File.open(path, 'wb') do |file|
158
161
  file.binmode
159
162
  http.get(uri.path) do |block|
160
163
  file.write(block)
161
164
  end
162
165
  end
163
- when "ssh"
166
+ when 'ssh'
164
167
  # TODO need better way to do conditional download
165
- # timestamp = `ssh #{uri.user + '@' if uri.user}#{uri.host} "ruby -e 'puts File.mtime(\\"#{uri.path}\\").to_i'"`.to_i
166
- timestamp = File.exist?(path) ? File.mtime(path).to_i : 0
168
+ timestamp = File.exist?(path) ? File.mtime(path).to_i : 0
169
+ # Requires that remote installation of fig be at the same location as the local machine.
167
170
  cmd = `which fig-download`.strip + " #{timestamp} #{uri.path}"
171
+ log_download(url, path)
168
172
  ssh_download(uri.user, uri.host, path, cmd)
169
173
  else
170
- $stderr.puts "Unknown protocol: #{url}"
171
- exit 10
174
+ Logging.fatal "Unknown protocol: #{url}"
175
+ raise NetworkError.new
172
176
  end
173
- end
174
-
177
+ end
178
+
175
179
  def download_resource(url, dir)
176
180
  FileUtils.mkdir_p(dir)
177
181
  download(url, File.join(dir, URI.parse(url).path.split('/').last))
178
182
  end
179
-
183
+
180
184
  def download_archive(url, dir)
181
185
  FileUtils.mkdir_p(dir)
182
186
  basename = URI.parse(url).path.split('/').last
@@ -192,25 +196,25 @@ module Fig
192
196
  when /\.zip$/
193
197
  unpack_archive(dir, path)
194
198
  else
195
- $stderr.puts "Unknown archive type: #{basename}"
196
- exit 10
199
+ Logging.fatal "Unknown archive type: #{basename}"
200
+ raise NetworkError.new
197
201
  end
198
202
  end
199
-
203
+
200
204
  def upload(local_file, remote_file, user)
201
- puts "uploading #{local_file} to #{remote_file}"
205
+ Logging.debug "Uploading #{local_file} to #{remote_file}."
202
206
  uri = URI.parse(remote_file)
203
207
  case uri.scheme
204
- when "ssh"
208
+ when 'ssh'
205
209
  ssh_upload(uri.user, uri.host, local_file, remote_file)
206
- when "ftp"
210
+ when 'ftp'
207
211
  # fail unless system "curl -T #{local_file} --create-dirs --ftp-create-dirs #{remote_file}"
208
212
  require 'net/ftp'
209
- ftp_uri = URI.parse(ENV["FIG_REMOTE_URL"])
213
+ ftp_uri = URI.parse(ENV['FIG_REMOTE_URL'])
210
214
  ftp_root_path = ftp_uri.path
211
- ftp_root_dirs = ftp_uri.path.split("/")
212
- remote_publish_path = uri.path[0, uri.path.rindex("/")]
213
- remote_publish_dirs = remote_publish_path.split("/")
215
+ ftp_root_dirs = ftp_uri.path.split('/')
216
+ remote_publish_path = uri.path[0, uri.path.rindex('/')]
217
+ remote_publish_dirs = remote_publish_path.split('/')
214
218
  # Use array subtraction to deduce which project/version folder to upload to,
215
219
  # i.e. [1,2,3] - [2,3,4] = [1]
216
220
  remote_project_dirs = remote_publish_dirs - ftp_root_dirs
@@ -231,26 +235,17 @@ module Fig
231
235
  end
232
236
  end
233
237
  end
234
-
238
+
235
239
  def clear_directory(dir)
236
240
  FileUtils.rm_rf(dir)
237
241
  FileUtils.mkdir_p(dir)
238
242
  end
239
243
 
240
- def exec(dir,command)
241
- Dir.chdir(dir) {
242
- unless system command
243
- $stderr.puts "Command failed"
244
- exit 10
245
- end
246
- }
247
- end
248
-
249
244
  def copy(source, target, msg = nil)
250
245
  if File.directory?(source)
251
246
  FileUtils.mkdir_p(target)
252
247
  Dir.foreach(source) do |child|
253
- if child != "." and child != ".."
248
+ if child != '.' and child != '..'
254
249
  copy(File.join(source, child), File.join(target, child), msg)
255
250
  end
256
251
  end
@@ -267,9 +262,9 @@ module Fig
267
262
  def move_file(dir, from, to)
268
263
  Dir.chdir(dir) { FileUtils.mv(from, to, :force => true) }
269
264
  end
270
-
265
+
271
266
  def log_info(msg)
272
- $stderr.puts msg
267
+ Logging.info msg
273
268
  end
274
269
 
275
270
  # Expects files_to_archive as an Array of filenames.
@@ -339,8 +334,8 @@ module Fig
339
334
  end
340
335
 
341
336
  def shell_exec_windows(cmd)
342
- #command = ["C:/WINDOWS/system32/cmd.exe", "/C", "call"] + cmd
343
- command = ["cmd.exe", "/C"] + cmd
337
+ #command = ['C:/WINDOWS/system32/cmd.exe', '/C', 'call'] + cmd
338
+ command = ['cmd.exe', '/C'] + cmd
344
339
  command = command.join(' ')
345
340
  Kernel.exec(command)
346
341
  end
@@ -349,13 +344,13 @@ module Fig
349
344
  # cmd = The command to be run on the remote host.
350
345
  def ssh_download(user, host, path, cmd)
351
346
  return_code = nil
352
- tempfile = Tempfile.new("tmp")
347
+ tempfile = Tempfile.new('tmp')
353
348
  Net::SSH.start(host, user) do |ssh|
354
349
  ssh.open_channel do |channel|
355
350
  channel.exec(cmd)
356
351
  channel.on_data() { |ch, data| tempfile << data }
357
- channel.on_extended_data() { |ch, type, data| $stderr.puts "SSH Download ERROR: #{data}" }
358
- channel.on_request("exit-status") { |ch, request|
352
+ channel.on_extended_data() { |ch, type, data| Logging.error "SSH Download ERROR: #{data}" }
353
+ channel.on_request('exit-status') { |ch, request|
359
354
  return_code = request.read_long
360
355
  }
361
356
  end
@@ -369,15 +364,14 @@ module Fig
369
364
  return false
370
365
  when NOT_FOUND
371
366
  tempfile.delete
372
- $stderr.puts "File not found: #{path}"
373
- exit 10
367
+ raise NotFoundError.new
374
368
  when SUCCESS
375
369
  FileUtils.mv(tempfile.path, path)
376
370
  return true
377
371
  else
378
372
  tempfile.delete
379
- $stderr.puts "Unable to download file: #{return_code}"
380
- exit 1
373
+ Logging.fatal "Unable to download file #{path}: #{return_code}"
374
+ raise NetworkError.new
381
375
  end
382
376
  end
383
377
 
@@ -392,5 +386,10 @@ module Fig
392
386
  end
393
387
  end
394
388
 
389
+ private
390
+
391
+ def log_download(url, path)
392
+ Logging.debug "Downloading #{url} to #{path}."
393
+ end
395
394
  end
396
395
  end