fig18 0.1.45-i386-mswin32
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 +55 -0
- data/LICENSE +27 -0
- data/README.md +283 -0
- data/VERSION +1 -0
- data/bin/fig +12 -0
- data/bin/fig-download +20 -0
- data/lib/fig/applicationconfiguration.rb +52 -0
- data/lib/fig/backtrace.rb +50 -0
- data/lib/fig/configfileerror.rb +15 -0
- data/lib/fig/environment.rb +216 -0
- data/lib/fig/figrc.rb +105 -0
- data/lib/fig/grammar.treetop +197 -0
- data/lib/fig/log4rconfigerror.rb +14 -0
- data/lib/fig/logging.rb +131 -0
- data/lib/fig/networkerror.rb +7 -0
- data/lib/fig/notfounderror.rb +4 -0
- data/lib/fig/options.rb +248 -0
- data/lib/fig/os.rb +416 -0
- data/lib/fig/package/archive.rb +24 -0
- data/lib/fig/package/command.rb +20 -0
- data/lib/fig/package/configuration.rb +40 -0
- data/lib/fig/package/include.rb +30 -0
- data/lib/fig/package/install.rb +21 -0
- data/lib/fig/package/override.rb +21 -0
- data/lib/fig/package/path.rb +21 -0
- data/lib/fig/package/publish.rb +21 -0
- data/lib/fig/package/resource.rb +24 -0
- data/lib/fig/package/retrieve.rb +21 -0
- data/lib/fig/package/set.rb +21 -0
- data/lib/fig/package/statement.rb +12 -0
- data/lib/fig/package.rb +73 -0
- data/lib/fig/packageerror.rb +7 -0
- data/lib/fig/parser.rb +37 -0
- data/lib/fig/repository.rb +252 -0
- data/lib/fig/repositoryerror.rb +7 -0
- data/lib/fig/retriever.rb +116 -0
- data/lib/fig/urlaccesserror.rb +13 -0
- data/lib/fig/userinputerror.rb +4 -0
- data/lib/fig/windows.rb +46 -0
- data/lib/fig.rb +230 -0
- metadata +336 -0
data/lib/fig/options.rb
ADDED
@@ -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
|