fig 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -19,6 +19,18 @@ Fig can be installed via rubygems. The gems are hosted at [Gemcutter](http://gem
19
19
  $ gem install gemcutter
20
20
  $ gem tumble
21
21
 
22
+ Fig also depends on a third-party library named, [libarchive](http://libarchive.rubyforge.org/). Libarchive is easily available
23
+ via most package management systems on Linux, FreeBSD, and OS X. Libarchive versions greater than 2.6.0 are preferred. If you are on Windows, the gem will install the libarchive binaries for you.
24
+
25
+ [Linux - Debian / Ubuntu]
26
+ apt-get libarchive-dev
27
+
28
+ [Linux - Red Hat / CentOS]
29
+ yum install libarchive-devel
30
+
31
+ [OS X - MacPorts]
32
+ port install libarchive
33
+
22
34
  Then you can install fig:
23
35
 
24
36
  $ gem install fig
data/bin/fig CHANGED
@@ -10,6 +10,7 @@ require 'fig/environment'
10
10
  require 'fig/repository'
11
11
  require 'fig/os'
12
12
  require 'fig/parser'
13
+ require 'fig/windows'
13
14
 
14
15
  include Fig
15
16
 
@@ -118,23 +119,27 @@ end
118
119
 
119
120
  if input
120
121
  package = Parser.new.parse_package(nil, nil, ".", input)
122
+ direct_retrieves=[]
121
123
  if options[:retrieve]
122
124
  package.retrieves.each do |var, path|
123
- env.add_retrieve(var, path)
125
+ if var =~ /^@([^\/]+)(.*)/
126
+ direct_retrieves << [$1, $2, path]
127
+ else
128
+ env.add_retrieve(var, path)
129
+ end
124
130
  end
125
131
  end
126
132
  unless options[:publish] || options[:list]
127
133
  env.register_package(package)
128
134
  env.apply_config(package, options[:config])
135
+ direct_retrieves.each do |info|
136
+ env.direct_retrieve(info[0], info[1], info[2])
137
+ end
129
138
  end
130
139
  else
131
140
  package = Package.new(nil, nil, ".", [])
132
141
  end
133
142
 
134
- def shell_exec(cmd)
135
- exec(ENV['SHELL'], '-c', cmd.join(' '))
136
- end
137
-
138
143
  if options[:publish]
139
144
  raise "Unexpected arguments: #{argv.join(' ')}" if !argv.empty?
140
145
  package_name, config_name, version_name = parse_descriptor(options[:publish])
@@ -154,12 +159,11 @@ elsif options[:echo]
154
159
  puts env[options[:echo]]
155
160
  elsif shell_command
156
161
  argv.shift
157
- env.execute_shell(shell_command) { |cmd| shell_exec cmd }
162
+ env.execute_shell(shell_command) { |cmd| os.shell_exec cmd }
158
163
  elsif argv[0]
159
164
  package_name, config_name, version_name = parse_descriptor(argv.shift)
160
165
  env.include_config(package, package_name, config_name, version_name)
161
- env.execute_config(package, package_name, config_name, nil, argv) { |cmd| shell_exec cmd }
166
+ env.execute_config(package, package_name, config_name, nil, argv) { |cmd| os.shell_exec cmd }
162
167
  elsif input
163
- env.execute_config(package, nil, options[:config], nil, argv) { |cmd| shell_exec cmd }
168
+ env.execute_config(package, nil, options[:config], nil, argv) { |cmd| os.shell_exec cmd }
164
169
  end
165
-
@@ -74,6 +74,12 @@ module Fig
74
74
  apply_config(package, config_name || "default")
75
75
  end
76
76
 
77
+ def direct_retrieve(package_name, source_path, target_path)
78
+ package = lookup_package(package_name, nil)
79
+ FileUtils.mkdir_p(target_path)
80
+ FileUtils.cp_r(File.join(package.directory, source_path, '.'), target_path)
81
+ end
82
+
77
83
  private
78
84
 
79
85
  def set_variable(base_package, name, value)
@@ -82,7 +88,16 @@ module Fig
82
88
 
83
89
  def append_variable(base_package, name, value)
84
90
  value = expand_value(base_package, name, value)
85
- prev = @variables[name]
91
+ # TODO: converting all environment variables to upcase is not a robust
92
+ # comparison. It also assumes all env vars will be in upcase
93
+ # in package.fig
94
+ prev = nil
95
+ @variables.each do |key, val|
96
+ if key.upcase == name.upcase
97
+ name = key
98
+ prev = val
99
+ end
100
+ end
86
101
  if prev
87
102
  @variables[name] = value + File::PATH_SEPARATOR + prev
88
103
  else
@@ -100,7 +115,6 @@ module Fig
100
115
  end
101
116
  end
102
117
 
103
-
104
118
  def lookup_package(package_name, version_name)
105
119
  package = @packages[package_name]
106
120
  if package.nil?
@@ -32,7 +32,7 @@ grammar Fig
32
32
  end
33
33
 
34
34
  rule retrieve
35
- "retrieve" ws var:[a-zA-Z0-9]+ "->" path:[a-zA-Z0-9/\.-\[\]]+ ws {
35
+ "retrieve" ws var:[@a-zA-Z0-9/\._]+ "->" path:[a-zA-Z0-9/\.-\[\]]+ ws {
36
36
  def to_package_statement
37
37
  Retrieve.new(var.text_value, path.text_value)
38
38
  end
@@ -1,6 +1,11 @@
1
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'
2
5
  require 'uri'
3
6
  require 'net/http'
7
+ require 'net/ssh'
8
+ require 'net/sftp'
4
9
  require 'tempfile'
5
10
 
6
11
  module Fig
@@ -80,35 +85,9 @@ module Fig
80
85
  when "ssh"
81
86
  # TODO need better way to do conditional download
82
87
  # timestamp = `ssh #{uri.user + '@' if uri.user}#{uri.host} "ruby -e 'puts File.mtime(\\"#{uri.path}\\").to_i'"`.to_i
83
- out = nil
84
88
  timestamp = File.exist?(path) ? File.mtime(path).to_i : 0
85
- tempfile = Tempfile.new("tmp")
86
- IO.popen("ssh #{uri.user + '@' if uri.user}#{uri.host} \"fig-download #{timestamp} #{uri.path}\"") do |io|
87
- first = true
88
- while bytes = io.read(4096)
89
- if first
90
- $stderr.puts "downloading #{url}"
91
- first = false
92
- end
93
- tempfile << bytes
94
- end
95
- end
96
- tempfile.close
97
- case $?.exitstatus
98
- when NOT_MODIFIED
99
- tempfile.delete
100
- return false
101
- when NOT_FOUND
102
- tempfile.delete
103
- raise "File not found: #{uri}"
104
- when SUCCESS
105
- FileUtils.mv(tempfile.path, path)
106
- return true
107
- else
108
- tempfile.delete
109
- $stderr.puts "Unable to download file: #{$?.exitstatus}"
110
- exit 1
111
- end
89
+ cmd = "fig-download #{timestamp} #{uri.path}"
90
+ ssh_download(uri.user, uri.host, path, cmd)
112
91
  else
113
92
  raise "Unknown protocol: #{url}"
114
93
  end
@@ -126,13 +105,13 @@ module Fig
126
105
  download(url, path)
127
106
  case basename
128
107
  when /\.tar\.gz$/
129
- fail unless system "tar -C #{dir} -zxf #{path}"
108
+ unpack_archive(dir, path)
130
109
  when /\.tgz$/
131
- fail unless system "tar -C #{dir} -zxf #{path}"
110
+ unpack_archive(dir, path)
132
111
  when /\.tar\.bz2$/
133
- fail unless system "tar -C #{dir} -jxf #{path}"
112
+ unpack_archive(dir, path)
134
113
  when /\.zip$/
135
- fail unless system "unzip -q -d #{dir} #{path}"
114
+ unpack_archive(dir, path)
136
115
  else
137
116
  raise "Unknown archive type: #{basename}"
138
117
  end
@@ -143,9 +122,7 @@ module Fig
143
122
  uri = URI.parse(remote_file)
144
123
  case uri.scheme
145
124
  when "ssh"
146
- dir = uri.path[0, uri.path.rindex('/')]
147
- cmd = "mkdir -p #{dir} && cat > #{uri.path}"
148
- fail unless system "cat #{local_file} | ssh #{uri.user + '@' if uri.user}#{uri.host} '#{cmd}'"
125
+ ssh_upload(uri.user, uri.host, local_file, remote_file)
149
126
  when "ftp"
150
127
  # fail unless system "curl -T #{local_file} --create-dirs --ftp-create-dirs #{remote_file}"
151
128
  require 'net/ftp'
@@ -172,8 +149,6 @@ module Fig
172
149
  end
173
150
  ftp.putbinaryfile(local_file)
174
151
  end
175
- else
176
- fail unless system "curl -p -T #{local_file} --create-dirs --ftp-create-dirs #{remote_file}"
177
152
  end
178
153
  end
179
154
 
@@ -184,16 +159,128 @@ module Fig
184
159
 
185
160
  def exec(dir,command)
186
161
  Dir.chdir(dir) { raise "Command failed" unless system command }
187
- end
162
+ end
188
163
 
189
164
  def copy(source, target)
190
165
  FileUtils.mkdir_p(File.dirname(target))
191
166
  FileUtils.copy_file(source, target)
192
167
  target
193
168
  end
169
+
170
+ def move_file(dir, from, to)
171
+ Dir.chdir(dir) { FileUtils.mv(from, to, :force => true) }
172
+ end
194
173
 
195
174
  def log_info(msg)
196
175
  puts msg
197
176
  end
177
+
178
+ # Expects files_to_archive as an Array of filenames.
179
+ def create_archive(archive_name, files_to_archive)
180
+ # TODO: Need to verify files_to_archive exists.
181
+ ::Archive.write_open_filename(archive_name, ::Archive::COMPRESSION_GZIP, ::Archive::FORMAT_TAR) do |ar|
182
+ files_to_archive.each do |fn|
183
+ ar.new_entry do |entry|
184
+ entry.copy_stat(fn)
185
+ entry.pathname = fn
186
+ ar.write_header(entry)
187
+ if !entry.directory?
188
+ ar.write_data(open(fn) {|f| f.read })
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ # This method can handle the following archive types:
196
+ # .tar.bz2
197
+ # .tar.gz
198
+ # .tgz
199
+ # .zip
200
+ def unpack_archive(dir, file)
201
+ Dir.chdir(dir) do
202
+ ::Archive.read_open_filename(file) do |ar|
203
+ while entry = ar.next_header
204
+ ar.extract(entry)
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ def self.windows?
211
+ Config::CONFIG['host_os'] =~ /mswin/
212
+ end
213
+
214
+ def self.unix?
215
+ !windows?
216
+ end
217
+
218
+ def shell_exec(cmd)
219
+ if OS.windows?
220
+ Windows.shell_exec_windows(cmd)
221
+ else
222
+ shell_exec_unix(cmd)
223
+ end
224
+ end
225
+
226
+ private
227
+
228
+ def shell_exec_unix(cmd)
229
+ Kernel.exec(ENV['SHELL'], '-c', cmd.join(' '))
230
+ end
231
+
232
+ def shell_exec_windows(cmd)
233
+ #command = ["C:/WINDOWS/system32/cmd.exe", "/C", "call"] + cmd
234
+ command = ["cmd.exe", "/C"] + cmd
235
+ command = command.join(' ')
236
+ Kernel.exec(command)
237
+ end
238
+
239
+ # path = The local path the file should be downloaded to.
240
+ # cmd = The command to be run on the remote host.
241
+ def ssh_download(user, host, path, cmd)
242
+ return_code = nil
243
+ tempfile = Tempfile.new("tmp")
244
+ Net::SSH.start(host, user) do |ssh|
245
+ ssh.open_channel do |channel|
246
+ channel.exec(cmd)
247
+ channel.on_data() { |ch, data| tempfile << data }
248
+ channel.on_extended_data() { |ch, type, data| $stderr.puts "SSH Download ERROR: #{data}" }
249
+ channel.on_request("exit-status") { |ch, request|
250
+ return_code = request.read_long
251
+ }
252
+ end
253
+ end
254
+
255
+ tempfile.close()
256
+
257
+ case return_code
258
+ when NOT_MODIFIED
259
+ tempfile.delete
260
+ return false
261
+ when NOT_FOUND
262
+ tempfile.delete
263
+ raise "File not found: #{uri}"
264
+ when SUCCESS
265
+ FileUtils.mv(tempfile.path, path)
266
+ return true
267
+ else
268
+ tempfile.delete
269
+ $stderr.puts "Unable to download file: #{return_code}"
270
+ exit 1
271
+ end
272
+ end
273
+
274
+ def ssh_upload(user, host, local_file, remote_file)
275
+ uri = URI.parse(remote_file)
276
+ dir = uri.path[0, uri.path.rindex('/')]
277
+ Net::SSH.start(host, user) do |ssh|
278
+ ssh.exec!("mkdir -p #{dir}")
279
+ end
280
+ Net::SFTP.start(host, user) do |sftp|
281
+ sftp.upload!(local_file, uri.path)
282
+ end
283
+ end
284
+
198
285
  end
199
286
  end
@@ -86,7 +86,7 @@ module Fig
86
86
  end
87
87
  if resources.size > 0
88
88
  file = "resources.tar.gz"
89
- file unless system "tar -zcf #{file} #{resources.join(' ')}"
89
+ @os.create_archive(file, resources.join(' '))
90
90
  new_package_statements.unshift(Archive.new(file))
91
91
  at_exit { File.delete(file) }
92
92
  end
@@ -179,7 +179,7 @@ module Fig
179
179
  @os.clear_directory(local_dir)
180
180
  # some packages contain no files, only a fig file.
181
181
  if not (package.archive_urls.empty? && package.resource_urls.empty?)
182
- @os.exec(temp_dir, "mv * #{local_dir}/")
182
+ FileUtils.mv(Dir.glob(File.join(temp_dir, "*")), local_dir)
183
183
  end
184
184
  write_local_package(package_name, version_name, package)
185
185
  rescue
@@ -0,0 +1,46 @@
1
+ # Keeping Windows-specific implementation details here.
2
+
3
+ require 'erb'
4
+ require 'fileutils'
5
+
6
+ # I don't know how to set environment variables so that a sub-shell will
7
+ # be able to use them. Therefore, I'm punting, and creating a batch script
8
+ # on the fly to run a user supplied command.
9
+
10
+ module Fig
11
+ class Windows
12
+
13
+ BATCH_SCRIPT_TEMPLATE = <<EOF
14
+ @echo off
15
+ % ENV.each do |k,v|
16
+ set <%= k %>=<%= v %>
17
+ % end
18
+
19
+ cmd /C <%= command %>
20
+ EOF
21
+
22
+
23
+ def self.with_generated_batch_script(cmd)
24
+ command = cmd.join(' ')
25
+ template = ERB.new(BATCH_SCRIPT_TEMPLATE, 0, "%")
26
+ output = template.result(binding)
27
+ begin
28
+ tf = File.new("C:/tmp/fig_command.bat", "w")
29
+ FileUtils.chmod(0755, tf.path)
30
+ File.open(tf.path, "w") do |fh|
31
+ fh.puts output
32
+ end
33
+ tf.close
34
+ yield tf.path
35
+ ensure
36
+ # tf.delete
37
+ end
38
+ end
39
+
40
+ def self.shell_exec_windows(cmd)
41
+ with_generated_batch_script(cmd) do |f|
42
+ Kernel.exec(f)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,17 +1,34 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  require 'rubygems'
4
- require 'open4'
4
+ require 'fig/os'
5
5
  require 'fileutils'
6
6
 
7
+ class Popen
8
+ if Fig::OS.windows?
9
+ require 'win32/open3'
10
+ def self.popen(*cmd)
11
+ Open3.popen3(*cmd) { |stdin,stdout,stderr|
12
+ yield stdin, stdout, stderr
13
+ }
14
+ end
15
+ else
16
+ require 'open4'
17
+ def self.popen(*cmd)
18
+ Open4::popen4(*cmd) { |pid, stdin, stdout, stderr|
19
+ yield stdin, stdout, stderr
20
+ }
21
+ end
22
+ end
23
+ end
24
+
7
25
  FIG_HOME = File.expand_path(File.dirname(__FILE__) + '/../tmp/fighome')
8
26
  FileUtils.mkdir_p(FIG_HOME)
9
27
  ENV['FIG_HOME'] = FIG_HOME
10
28
 
11
29
  FIG_REMOTE_DIR = File.expand_path(File.dirname(__FILE__) + '/../tmp/remote')
12
30
  FileUtils.mkdir_p(FIG_REMOTE_DIR)
13
- ENV['FIG_REMOTE_URL'] = "ssh://localhost#{FIG_REMOTE_DIR}"
14
- puts ENV['FIG_REMOTE_URL']
31
+ ENV['FIG_REMOTE_URL'] = "ssh://#{ENV['USER']}@localhost#{FIG_REMOTE_DIR}"
15
32
 
16
33
  FIG_EXE = File.expand_path(File.dirname(__FILE__) + '/../bin/fig')
17
34
 
@@ -19,7 +36,7 @@ def fig(args, input=nil)
19
36
  args = "--file - #{args}" if input
20
37
  out = nil
21
38
  err = nil
22
- status = Open4::popen4("#{FIG_EXE} #{args}") do |pid, stdin, stdout, stderr|
39
+ Popen.popen("#{FIG_EXE} #{args}") do |stdin, stdout, stderr|
23
40
  if input
24
41
  stdin.puts input
25
42
  stdin.close
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'fig/os'
3
+ require 'fig/windows'
4
+
5
+
6
+ # Only run on Windows...
7
+ if Fig::OS.windows?
8
+ describe "Fig on Windows" do
9
+ it "batch script should exist" do
10
+ Fig::Windows.with_generated_batch_script(["echo", "Hello World"]) do |filename|
11
+ File.exist?(filename).should == true
12
+ end
13
+ end
14
+
15
+ it "batch script should say 'Hello World' when executed" do
16
+ Fig::Windows.with_generated_batch_script(["echo", "Hello World"]) do |filename|
17
+ %x[#{filename}].should == "Hello World\n"
18
+ end
19
+ end
20
+ end
21
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fig
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Foemmel
@@ -9,9 +9,39 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-09 00:00:00 +00:00
12
+ date: 2010-02-18 00:00:00 -06:00
13
13
  default_executable:
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: libarchive
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: net-ssh
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.15
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: net-sftp
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.0.2
44
+ version:
15
45
  - !ruby/object:Gem::Dependency
16
46
  name: polyglot
17
47
  type: :runtime
@@ -73,6 +103,7 @@ files:
73
103
  - lib/fig/package.rb
74
104
  - lib/fig/parser.rb
75
105
  - lib/fig/repository.rb
106
+ - lib/fig/windows.rb
76
107
  - LICENSE
77
108
  - README.md
78
109
  has_rdoc: true
@@ -104,5 +135,6 @@ signing_key:
104
135
  specification_version: 3
105
136
  summary: Fig is a utility for configuring environments and managing dependencies across a team of developers..
106
137
  test_files:
107
- - spec/spec_helper.rb
108
138
  - spec/fig_spec.rb
139
+ - spec/spec_helper.rb
140
+ - spec/win_spec.rb