fig 0.1.24-universal-darwin9.0

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.rb ADDED
File without changes
@@ -0,0 +1,171 @@
1
+ module Fig
2
+
3
+ # This class manages the program's state, including the value of all environment
4
+ # variables, and which packages have already been applied
5
+ class Environment
6
+ DEFAULT_VERSION_NAME = "current"
7
+
8
+ def initialize(os, repository, variables)
9
+ @os = os
10
+ @repository = repository
11
+ @variables = variables
12
+ @retrieve_vars = {}
13
+ @packages = {}
14
+ @applied_configs = {}
15
+ end
16
+
17
+ # Returns the value of an envirionment variable
18
+ def [](name)
19
+ @variables[name]
20
+ end
21
+
22
+ # Indicates that the values from a particular envrionment variable path should
23
+ # be copied to a local directory
24
+ def add_retrieve(name, path)
25
+ @retrieve_vars[name] = path
26
+ end
27
+
28
+ def register_package(package)
29
+ name = package.package_name
30
+ if @packages[name]
31
+ puts "Package already exists with name: #{name}"
32
+ exit 10
33
+ end
34
+ @packages[name] = package
35
+ end
36
+
37
+ def apply_config(package, config_name)
38
+ if (@applied_configs[package.package_name] ||= []).member?(config_name)
39
+ return
40
+ end
41
+ config = package[config_name]
42
+ config.statements.each { |stmt| apply_config_statement(package, stmt) }
43
+ @applied_configs[package.package_name] << config_name
44
+ end
45
+
46
+ def execute_shell(command)
47
+ with_environment do
48
+ yield command.map{|arg| expand_arg(arg)}
49
+ end
50
+ end
51
+
52
+ def execute_config(base_package, package_name, config_name, version_name, args)
53
+ package = lookup_package(package_name || base_package.package_name, version_name)
54
+ result = nil
55
+ commands = package[config_name || "default"].commands
56
+ with_environment do
57
+ # todo nil check
58
+ commands.each do |command|
59
+ result = yield expand_arg("#{command.command} #{args.join(' ')}").gsub("@",package.directory).split(" ")
60
+ end
61
+ end
62
+ result
63
+ end
64
+
65
+ def apply_config_statement(base_package, statement)
66
+ case statement
67
+ when Path
68
+ append_variable(base_package, statement.name, statement.value)
69
+ when Set
70
+ set_variable(base_package, statement.name, statement.value)
71
+ when Include
72
+ include_config(base_package, statement.package_name, statement.config_name, statement.version_name)
73
+ when Command
74
+ # ignore
75
+ else
76
+ fail "Unexpected statement: #{statement}"
77
+ end
78
+ end
79
+
80
+ def include_config(base_package, package_name, config_name, version_name)
81
+ package = lookup_package(package_name || base_package.package_name, version_name)
82
+ apply_config(package, config_name || "default")
83
+ end
84
+
85
+ def direct_retrieve(package_name, source_path, target_path)
86
+ package = lookup_package(package_name, nil)
87
+ FileUtils.mkdir_p(target_path)
88
+ FileUtils.cp_r(File.join(package.directory, source_path, '.'), target_path)
89
+ end
90
+
91
+ private
92
+
93
+ def set_variable(base_package, name, value)
94
+ @variables[name] = expand_value(base_package, name, value)
95
+ end
96
+
97
+ def append_variable(base_package, name, value)
98
+ value = expand_value(base_package, name, value)
99
+ # TODO: converting all environment variables to upcase is not a robust
100
+ # comparison. It also assumes all env vars will be in upcase
101
+ # in package.fig
102
+ prev = nil
103
+ @variables.each do |key, val|
104
+ if key.upcase == name.upcase
105
+ name = key
106
+ prev = val
107
+ end
108
+ end
109
+ if prev
110
+ @variables[name] = value + File::PATH_SEPARATOR + prev
111
+ else
112
+ @variables[name] = value
113
+ end
114
+ end
115
+
116
+ def with_environment
117
+ old_env = {}
118
+ begin
119
+ @variables.each { |key,value| old_env[key] = ENV[key]; ENV[key] = value }
120
+ yield
121
+ ensure
122
+ old_env.each { |key,value| ENV[key] = value }
123
+ end
124
+ end
125
+
126
+ def lookup_package(package_name, version_name)
127
+ package = @packages[package_name]
128
+ if package.nil?
129
+ package = @repository.load_package(package_name, version_name || DEFAULT_VERSION_NAME)
130
+ @packages[package_name] = package
131
+ elsif version_name && version_name != package.version_name
132
+ puts "Version mismatch: #{package_name}"
133
+ exit 10
134
+ end
135
+ package
136
+ end
137
+
138
+ # Replace @ symbol with the package's directory
139
+ def expand_value(base_package, name, value)
140
+ return value unless base_package && base_package.package_name
141
+ file = value.gsub(/\@/, base_package.directory)
142
+ if @retrieve_vars.member?(name)
143
+ # A '//' in the source file's path tells us to preserve path information
144
+ # after the '//' when doing a retrieve.
145
+ if file.split('//').size > 1
146
+ preserved_path = file.split('//').last
147
+ target = File.join(@retrieve_vars[name].gsub(/\[package\]/, base_package.package_name), preserved_path)
148
+ else
149
+ target = File.join(@retrieve_vars[name].gsub(/\[package\]/, base_package.package_name), File.basename(file))
150
+ end
151
+ unless @os.exist?(target) && @os.mtime(target) >= @os.mtime(file)
152
+ @os.log_info("retrieving #{target}")
153
+ @os.copy(file, target)
154
+ end
155
+ file = target
156
+ end
157
+ file
158
+ end
159
+
160
+ def expand_arg(arg)
161
+ arg.gsub(/\@([a-zA-Z0-9\-\.]+)/) do |match|
162
+ package = @packages[$1]
163
+ if package.nil?
164
+ puts "Package not found: #{$1}"
165
+ exit 10
166
+ end
167
+ package.directory
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,141 @@
1
+ require 'fig/package'
2
+
3
+ module Fig
4
+
5
+ grammar Fig
6
+ rule package
7
+ ws statements:(package_statement*) {
8
+ def to_package(package_name, version_name, directory)
9
+ Package.new(package_name, version_name, directory, statements.elements.map { |statement| statement.to_package_statement })
10
+ end
11
+ }
12
+ end
13
+
14
+ rule package_statement
15
+ archive / resource / retrieve / config
16
+ end
17
+
18
+ rule archive
19
+ "archive" ws url {
20
+ def to_package_statement
21
+ Archive.new(url.value.text_value)
22
+ end
23
+ }
24
+ end
25
+
26
+ rule resource
27
+ "resource" ws url {
28
+ def to_package_statement
29
+ Resource.new(url.value.text_value)
30
+ end
31
+ }
32
+ end
33
+
34
+ rule retrieve
35
+ "retrieve" ws var:[@a-zA-Z0-9/\._]+ "->" path:[a-zA-Z0-9/\.\-\[\]]+ ws {
36
+ def to_package_statement
37
+ Retrieve.new(var.text_value, path.text_value)
38
+ end
39
+ }
40
+ end
41
+
42
+ rule install
43
+ "install" ws statements:config_statement* "end" ws {
44
+ def to_package_statement
45
+ Install.new(statements.elements.map { |statement| statement.to_config_statement })
46
+ end
47
+ }
48
+ end
49
+
50
+ rule config
51
+ "config" ws config_name ws statements:config_statement* "end" ws {
52
+ def to_package_statement
53
+ Configuration.new(config_name.text_value, statements.elements.map { |statement| statement.to_config_statement })
54
+ end
55
+ }
56
+ end
57
+
58
+ rule config_statement
59
+ include / command / path / set
60
+ end
61
+
62
+ rule include
63
+ "include" ws descriptor {
64
+ def to_config_statement
65
+ package = descriptor.respond_to?(:package) ? descriptor.package.text_value : nil
66
+ config = descriptor.get_config
67
+ version = descriptor.get_version
68
+ Include.new(package, config, version)
69
+ end
70
+ }
71
+ end
72
+
73
+ rule path
74
+ ("append" / "path" / "add") ws name:[a-zA-Z0-9_]+ "=" value:[@a-zA-Z0-9/\-\\._]+ ws {
75
+ def to_config_statement
76
+ Path.new(name.text_value, value.text_value)
77
+ end
78
+ }
79
+ end
80
+
81
+ rule set
82
+ "set" ws name:[a-zA-Z0-9_]+ "=" value:[@a-zA-Z0-9/\-\\._]+ ws {
83
+ def to_config_statement
84
+ Set.new(name.text_value, value.text_value)
85
+ end
86
+ }
87
+ end
88
+
89
+ rule command
90
+ "command" ws string {
91
+ def to_config_statement
92
+ Command.new(string.value.text_value)
93
+ end
94
+ }
95
+ end
96
+
97
+ rule string
98
+ '"' value:(!'"' . )* '"' ws
99
+ end
100
+
101
+ rule descriptor
102
+ ((package:[a-zA-Z0-9.-]+ ("/" version:[a-zA-Z0-9_\-.]+)? (":" config:config_name)? ws) /
103
+ (":" config:config_name ws)) {
104
+ def get_version
105
+ elements.each do |element|
106
+ if element.respond_to?(:version)
107
+ return element.version.text_value
108
+ end
109
+ end
110
+ nil
111
+ end
112
+ def get_config
113
+ return self.config.text_value if self.respond_to?(:config)
114
+ elements.each do |element|
115
+ if element.respond_to?(:config)
116
+ return element.config.text_value
117
+ end
118
+ end
119
+ nil
120
+ end
121
+ }
122
+ end
123
+
124
+ rule config_name
125
+ [a-zA-Z0-9_\-.]+
126
+ end
127
+
128
+ rule name
129
+ value:[a-zA-Z0-9]+ ws
130
+ end
131
+
132
+ rule url
133
+ (value:[a-zA-Z0-9:/\-\\._\*]+ ws) / ('"' value:[a-zA-Z0-9:/\-\\._]+ '"' ws)
134
+ end
135
+
136
+ rule ws
137
+ [ \n\r\t]+
138
+ end
139
+ end
140
+
141
+ end
@@ -0,0 +1,98 @@
1
+ require 'optparse'
2
+ require 'fig/package'
3
+
4
+ module Fig
5
+ def parse_descriptor(descriptor)
6
+ # todo should use treetop for these:
7
+ package_name = descriptor =~ /^([^:\/]+)/ ? $1 : nil
8
+ config_name = descriptor =~ /:([^:\/]+)/ ? $1 : nil
9
+ version_name = descriptor =~ /\/([^:\/]+)/ ? $1 : nil
10
+ return package_name, config_name, version_name
11
+ end
12
+
13
+ def parse_options(argv)
14
+ options = {}
15
+
16
+ parser = OptionParser.new do |opts|
17
+ opts.banner = "Usage: fig [--debug] [--update] [--config <config>] [--get <var> | --list | <package> | - <command>]"
18
+
19
+ opts.on('-?', '-h','--help','display this help text') do
20
+ puts opts
21
+ exit 1
22
+ end
23
+
24
+ options[:debug] = false
25
+ opts.on('-d', '--debug', 'print debug info') { options[:debug] = true }
26
+
27
+ options[:update] = false
28
+ opts.on('-u', '--update', 'check remote repository for updates') { options[:update] = true; options[:retrieve] = true }
29
+
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 }
32
+
33
+ options[:config] = "default"
34
+ opts.on('-c', '--config CFG', 'name of configuration to apply') { |config| options[:config] = config }
35
+
36
+ options[:echo] = nil
37
+ opts.on('-g', '--get VAR', 'print value of environment variable') { |echo| options[:echo] = echo }
38
+
39
+ options[:publish] = nil
40
+ opts.on('--publish PKG', 'install package in local and remote repositories') { |publish| options[:publish] = publish }
41
+
42
+ options[:publish_local] = nil
43
+ opts.on('--publish-local PKG', 'install package in local repositorie only') { |publish_local| options[:publish_local] = publish_local }
44
+
45
+ options[:force] = nil
46
+ opts.on('--force', 'force overwriting of an existing remote package version') { |force| options[:force] = force }
47
+
48
+ options[:resources] =[]
49
+ opts.on('--resource PATH', 'resource to include in package (when using --publish)') do |path|
50
+ options[:resources] << Resource.new(path)
51
+ end
52
+
53
+ options[:archives] =[]
54
+ opts.on('--archive PATH', 'archive to include in package (when using --publish)') do |path|
55
+ options[:archives] << Archive.new(path)
56
+ end
57
+
58
+ options[:list] = false
59
+ opts.on('--list', 'list packages in local repository') { options[:list] = true }
60
+
61
+ options[:list_remote] = false
62
+ opts.on('--list-remote', 'list packages in remote repository') { options[:list_remote] = true }
63
+
64
+ options[:list_configs] = []
65
+ opts.on('--list-configs PKG', 'list configurations in package') { |descriptor| options[:list_configs] << descriptor }
66
+
67
+ options[:cleans] = []
68
+ opts.on('--clean PKG', 'remove package from local repository') { |descriptor| options[:cleans] << descriptor }
69
+
70
+ options[:modifiers] = []
71
+
72
+ opts.on('-i', '--include PKG', 'include package in environment') do |descriptor|
73
+ package_name, config_name, version_name = parse_descriptor(descriptor)
74
+ options[:modifiers] << Include.new(package_name, config_name, version_name)
75
+ end
76
+
77
+ opts.on('-s', '--set VAR=VAL', 'set environment variable') do |var_val|
78
+ var, val = var_val.split('=')
79
+ options[:modifiers] << Set.new(var, val)
80
+ end
81
+
82
+ opts.on('-p', '--append VAR=VAL', 'append environment variable') do |var_val|
83
+ var, val = var_val.split('=')
84
+ options[:modifiers] << Path.new(var, val)
85
+ end
86
+
87
+ options[:input] = nil
88
+ opts.on('--file FILE', 'fig file to read (use - for stdin)') { |path| options[:input] = path }
89
+ opts.on('--no-file', 'ignore package.fig file in current directory') { |path| options[:input] = :none }
90
+
91
+ options[:home] = ENV['FIG_HOME'] || File.expand_path("~/.fighome")
92
+ end
93
+
94
+ parser.parse!(argv)
95
+
96
+ return options, argv
97
+ end
98
+ end
data/lib/fig/os.rb ADDED
@@ -0,0 +1,325 @@
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 'tempfile'
10
+
11
+ module Fig
12
+ class NotFoundException < Exception
13
+ end
14
+
15
+ class OS
16
+ def list(dir)
17
+ Dir.entries(dir) - ['.','..']
18
+ end
19
+
20
+ def exist?(path)
21
+ File.exist?(path)
22
+ end
23
+
24
+ def mtime(path)
25
+ File.mtime(path)
26
+ end
27
+
28
+ def read(path)
29
+ File.read(path)
30
+ end
31
+
32
+ def write(path, content)
33
+ File.open(path, "wb") { |f| f.binmode; f << content }
34
+ end
35
+
36
+ SUCCESS = 0
37
+ NOT_MODIFIED = 3
38
+ NOT_FOUND = 4
39
+
40
+ def download_list(url)
41
+ begin
42
+ uri = URI.parse(url)
43
+ rescue
44
+ puts "Unable to parse url: '#{url}'"
45
+ exit 10
46
+ end
47
+ case uri.scheme
48
+ when "ftp"
49
+ ftp = Net::FTP.new(uri.host)
50
+ ftp.login
51
+ ftp.chdir(uri.path)
52
+ packages = []
53
+ ftp.retrlines('LIST -R .') do |line|
54
+ parts = line.gsub(/\\/, '/').sub(/^\.\//, '').sub(/:$/, '').split('/')
55
+ packages << parts.join('/') if parts.size == 2
56
+ end
57
+ ftp.close
58
+ packages
59
+ when "ssh"
60
+ packages = []
61
+ Net::SSH.start(uri.host, uri.user) do |ssh|
62
+ ls = ssh.exec!("[ -d #{uri.path} ] && find #{uri.path}")
63
+ if not ls.nil?
64
+ ls = ls.gsub(uri.path + "/", "").gsub(uri.path, "")
65
+ ls.each do |line|
66
+ parts = line.gsub(/\\/, '/').sub(/^\.\//, '').sub(/:$/, '').chomp().split('/')
67
+ packages << parts.join('/') if parts.size == 2
68
+ end
69
+ end
70
+ end
71
+ packages
72
+ else
73
+ puts "Protocol not supported: #{url}"
74
+ exit 10
75
+ end
76
+ end
77
+
78
+ def download(url, path)
79
+ FileUtils.mkdir_p(File.dirname(path))
80
+ uri = URI.parse(url)
81
+ case uri.scheme
82
+ when "ftp"
83
+ ftp = Net::FTP.new(uri.host)
84
+ ftp.login
85
+ begin
86
+ if File.exist?(path) && ftp.mtime(uri.path) <= File.mtime(path)
87
+ return false
88
+ else
89
+ puts "downloading #{url}"
90
+ ftp.getbinaryfile(uri.path, path, 256*1024)
91
+ return true
92
+ end
93
+ rescue Net::FTPPermError
94
+ raise NotFoundException.new
95
+ end
96
+ when "http"
97
+ http = Net::HTTP.new(uri.host)
98
+ puts "downloading #{url}"
99
+ File.open(path, "wb") do |file|
100
+ file.binmode
101
+ http.get(uri.path) do |block|
102
+ file.write(block)
103
+ end
104
+ end
105
+ when "ssh"
106
+ # TODO need better way to do conditional download
107
+ # timestamp = `ssh #{uri.user + '@' if uri.user}#{uri.host} "ruby -e 'puts File.mtime(\\"#{uri.path}\\").to_i'"`.to_i
108
+ timestamp = File.exist?(path) ? File.mtime(path).to_i : 0
109
+ cmd = `which fig-download`.strip + " #{timestamp} #{uri.path}"
110
+ ssh_download(uri.user, uri.host, path, cmd)
111
+ else
112
+ puts "Unknown protocol: #{url}"
113
+ exit 10
114
+ end
115
+ end
116
+
117
+ def download_resource(url, dir)
118
+ FileUtils.mkdir_p(dir)
119
+ download(url, File.join(dir, URI.parse(url).path.split('/').last))
120
+ end
121
+
122
+ def download_archive(url, dir)
123
+ FileUtils.mkdir_p(dir)
124
+ basename = URI.parse(url).path.split('/').last
125
+ path = File.join(dir, basename)
126
+ download(url, path)
127
+ case basename
128
+ when /\.tar\.gz$/
129
+ unpack_archive(dir, path)
130
+ when /\.tgz$/
131
+ unpack_archive(dir, path)
132
+ when /\.tar\.bz2$/
133
+ unpack_archive(dir, path)
134
+ when /\.zip$/
135
+ unpack_archive(dir, path)
136
+ else
137
+ puts "Unknown archive type: #{basename}"
138
+ exit 10
139
+ end
140
+ end
141
+
142
+ def upload(local_file, remote_file, user)
143
+ puts "uploading #{local_file} to #{remote_file}"
144
+ uri = URI.parse(remote_file)
145
+ case uri.scheme
146
+ when "ssh"
147
+ ssh_upload(uri.user, uri.host, local_file, remote_file)
148
+ when "ftp"
149
+ # fail unless system "curl -T #{local_file} --create-dirs --ftp-create-dirs #{remote_file}"
150
+ require 'net/ftp'
151
+ ftp_uri = URI.parse(ENV["FIG_REMOTE_URL"])
152
+ ftp_root_path = ftp_uri.path
153
+ ftp_root_dirs = ftp_uri.path.split("/")
154
+ remote_publish_path = uri.path[0, uri.path.rindex("/")]
155
+ remote_publish_dirs = remote_publish_path.split("/")
156
+ # Use array subtraction to deduce which project/version folder to upload to,
157
+ # i.e. [1,2,3] - [2,3,4] = [1]
158
+ remote_project_dirs = remote_publish_dirs - ftp_root_dirs
159
+ Net::FTP.open(uri.host) do |ftp|
160
+ ftp.login
161
+ # Assume that the FIG_REMOTE_URL path exists.
162
+ ftp.chdir(ftp_root_path)
163
+ remote_project_dirs.each do |dir|
164
+ # Can't automatically create parent directories, so do it manually.
165
+ if ftp.nlst().index(dir).nil?
166
+ ftp.mkdir(dir)
167
+ ftp.chdir(dir)
168
+ else
169
+ ftp.chdir(dir)
170
+ end
171
+ end
172
+ ftp.putbinaryfile(local_file)
173
+ end
174
+ end
175
+ end
176
+
177
+ def clear_directory(dir)
178
+ FileUtils.rm_rf(dir)
179
+ FileUtils.mkdir_p(dir)
180
+ end
181
+
182
+ def exec(dir,command)
183
+ Dir.chdir(dir) {
184
+ unless system command
185
+ puts "Command failed"
186
+ exit 10
187
+ end
188
+ }
189
+ end
190
+
191
+ def copy(source, target)
192
+ FileUtils.mkdir_p(File.dirname(target))
193
+ FileUtils.cp_r(source, target)
194
+ end
195
+
196
+ def move_file(dir, from, to)
197
+ Dir.chdir(dir) { FileUtils.mv(from, to, :force => true) }
198
+ end
199
+
200
+ def log_info(msg)
201
+ $stderr.puts msg
202
+ end
203
+
204
+ # Expects files_to_archive as an Array of filenames.
205
+ def create_archive(archive_name, files_to_archive)
206
+ if OS.java?
207
+ `tar czvf #{archive_name} #{files_to_archive.join(' ')}`
208
+ else
209
+ # TODO: Need to verify files_to_archive exists.
210
+ ::Archive.write_open_filename(archive_name, ::Archive::COMPRESSION_GZIP, ::Archive::FORMAT_TAR) do |ar|
211
+ files_to_archive.each do |fn|
212
+ ar.new_entry do |entry|
213
+ entry.copy_stat(fn)
214
+ entry.pathname = fn
215
+ ar.write_header(entry)
216
+ if !entry.directory?
217
+ ar.write_data(open(fn) {|f| f.binmode; f.read })
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+
225
+ # This method can handle the following archive types:
226
+ # .tar.bz2
227
+ # .tar.gz
228
+ # .tgz
229
+ # .zip
230
+ def unpack_archive(dir, file)
231
+ Dir.chdir(dir) do
232
+ if OS.java?
233
+ `tar xzvf #{file}`
234
+ else
235
+ ::Archive.read_open_filename(file) do |ar|
236
+ while entry = ar.next_header
237
+ ar.extract(entry)
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ def self.windows?
245
+ Config::CONFIG['host_os'] =~ /mswin|mingw/
246
+ end
247
+
248
+ def self.java?
249
+ RUBY_PLATFORM == 'java'
250
+ end
251
+
252
+ def self.unix?
253
+ !windows?
254
+ end
255
+
256
+ def shell_exec(cmd)
257
+ if OS.windows?
258
+ Windows.shell_exec_windows(cmd)
259
+ else
260
+ shell_exec_unix(cmd)
261
+ end
262
+ end
263
+
264
+ private
265
+
266
+ def shell_exec_unix(cmd)
267
+ Kernel.exec(ENV['SHELL'], '-c', cmd.join(' '))
268
+ end
269
+
270
+ def shell_exec_windows(cmd)
271
+ #command = ["C:/WINDOWS/system32/cmd.exe", "/C", "call"] + cmd
272
+ command = ["cmd.exe", "/C"] + cmd
273
+ command = command.join(' ')
274
+ Kernel.exec(command)
275
+ end
276
+
277
+ # path = The local path the file should be downloaded to.
278
+ # cmd = The command to be run on the remote host.
279
+ def ssh_download(user, host, path, cmd)
280
+ return_code = nil
281
+ tempfile = Tempfile.new("tmp")
282
+ Net::SSH.start(host, user) do |ssh|
283
+ ssh.open_channel do |channel|
284
+ channel.exec(cmd)
285
+ channel.on_data() { |ch, data| tempfile << data }
286
+ channel.on_extended_data() { |ch, type, data| $stderr.puts "SSH Download ERROR: #{data}" }
287
+ channel.on_request("exit-status") { |ch, request|
288
+ return_code = request.read_long
289
+ }
290
+ end
291
+ end
292
+
293
+ tempfile.close()
294
+
295
+ case return_code
296
+ when NOT_MODIFIED
297
+ tempfile.delete
298
+ return false
299
+ when NOT_FOUND
300
+ tempfile.delete
301
+ puts "File not found: #{path}"
302
+ exit 10
303
+ when SUCCESS
304
+ FileUtils.mv(tempfile.path, path)
305
+ return true
306
+ else
307
+ tempfile.delete
308
+ $stderr.puts "Unable to download file: #{return_code}"
309
+ exit 1
310
+ end
311
+ end
312
+
313
+ def ssh_upload(user, host, local_file, remote_file)
314
+ uri = URI.parse(remote_file)
315
+ dir = uri.path[0, uri.path.rindex('/')]
316
+ Net::SSH.start(host, user) do |ssh|
317
+ ssh.exec!("mkdir -p #{dir}")
318
+ end
319
+ Net::SFTP.start(host, user) do |sftp|
320
+ sftp.upload!(local_file, uri.path)
321
+ end
322
+ end
323
+
324
+ end
325
+ end