fig18 0.1.39 → 0.1.40

Sign up to get free protection for your applications and to get access to all the features.
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