engineyard-serverside 1.6.0.pre5 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/lib/engineyard-serverside.rb +1 -3
  2. data/lib/engineyard-serverside/cli.rb +38 -73
  3. data/lib/engineyard-serverside/configuration.rb +12 -38
  4. data/lib/engineyard-serverside/deploy.rb +54 -63
  5. data/lib/engineyard-serverside/deploy_hook.rb +18 -21
  6. data/lib/engineyard-serverside/deprecation.rb +17 -9
  7. data/lib/engineyard-serverside/lockfile_parser.rb +1 -1
  8. data/lib/engineyard-serverside/logged_output.rb +91 -0
  9. data/lib/engineyard-serverside/rails_asset_support.rb +5 -5
  10. data/lib/engineyard-serverside/server.rb +11 -8
  11. data/lib/engineyard-serverside/strategies/git.rb +15 -12
  12. data/lib/engineyard-serverside/task.rb +8 -29
  13. data/lib/engineyard-serverside/version.rb +1 -1
  14. data/lib/vendor/systemu/LICENSE +3 -0
  15. data/lib/vendor/systemu/lib/systemu.rb +363 -0
  16. data/lib/vendor/systemu/systemu.gemspec +45 -0
  17. data/spec/basic_deploy_spec.rb +9 -9
  18. data/spec/bundler_deploy_spec.rb +1 -1
  19. data/spec/custom_deploy_spec.rb +4 -63
  20. data/spec/deploy_hook_spec.rb +78 -77
  21. data/spec/deprecation_spec.rb +26 -4
  22. data/spec/git_strategy_spec.rb +2 -6
  23. data/spec/logged_output_spec.rb +55 -0
  24. data/spec/nodejs_deploy_spec.rb +2 -2
  25. data/spec/services_deploy_spec.rb +10 -11
  26. data/spec/spec_helper.rb +25 -48
  27. data/spec/sqlite3_deploy_spec.rb +2 -1
  28. data/spec/support/integration.rb +14 -2
  29. metadata +79 -94
  30. data/lib/engineyard-serverside/shell.rb +0 -102
  31. data/lib/engineyard-serverside/shell/formatter.rb +0 -73
  32. data/lib/engineyard-serverside/shell/helpers.rb +0 -29
  33. data/lib/vendor/open4/lib/open4.rb +0 -432
  34. data/spec/shell_spec.rb +0 -50
@@ -0,0 +1,363 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tmpdir'
4
+ require 'socket'
5
+ require 'fileutils'
6
+ require 'rbconfig'
7
+ require 'thread'
8
+
9
+ class Object
10
+ def systemu(*a, &b) SystemUniversal.new(*a, &b).systemu end
11
+ end
12
+
13
+ class SystemUniversal
14
+ #
15
+ # constants
16
+ #
17
+ SystemUniversal::VERSION = '2.5.0' unless SystemUniversal.send(:const_defined?, :VERSION)
18
+ def SystemUniversal.version() SystemUniversal::VERSION end
19
+ def version() SystemUniversal::VERSION end
20
+ #
21
+ # class methods
22
+ #
23
+
24
+ @host = Socket.gethostname
25
+ @ppid = Process.ppid
26
+ @pid = Process.pid
27
+ @turd = ENV['SYSTEMU_TURD']
28
+
29
+ c = begin; ::RbConfig::CONFIG; rescue NameError; ::Config::CONFIG; end
30
+ ruby = File.join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
31
+ @ruby = if system('%s -e 42' % ruby)
32
+ ruby
33
+ else
34
+ system('%s -e 42' % 'ruby') ? 'ruby' : warn('no ruby in PATH/CONFIG')
35
+ end
36
+
37
+ class << SystemUniversal
38
+ %w( host ppid pid ruby turd ).each{|a| attr_accessor a}
39
+
40
+ def quote(*words)
41
+ words.map{|word| word.inspect}.join(' ')
42
+ end
43
+ end
44
+
45
+ #
46
+ # instance methods
47
+ #
48
+
49
+ def initialize argv, opts = {}, &block
50
+ getopt = getopts opts
51
+
52
+ @argv = argv
53
+ @block = block
54
+
55
+ @stdin = getopt[ ['stdin', 'in', '0', 0] ]
56
+ @stdout = getopt[ ['stdout', 'out', '1', 1] ]
57
+ @stderr = getopt[ ['stderr', 'err', '2', 2] ]
58
+ @env = getopt[ 'env' ]
59
+ @cwd = getopt[ 'cwd' ]
60
+
61
+ @host = getopt[ 'host', self.class.host ]
62
+ @ppid = getopt[ 'ppid', self.class.ppid ]
63
+ @pid = getopt[ 'pid', self.class.pid ]
64
+ @ruby = getopt[ 'ruby', self.class.ruby ]
65
+ end
66
+
67
+ def systemu
68
+ tmpdir do |tmp|
69
+ c = child_setup tmp
70
+ status = nil
71
+
72
+ begin
73
+ thread = nil
74
+
75
+ quietly{
76
+ IO.popen "#{ quote(@ruby) } #{ quote(c['program']) }", 'r+' do |pipe|
77
+ line = pipe.gets
78
+ case line
79
+ when %r/^pid: \d+$/
80
+ cid = Integer line[%r/\d+/]
81
+ else
82
+ begin
83
+ buf = pipe.read
84
+ buf = "#{ line }#{ buf }"
85
+ e = Marshal.load buf
86
+ raise unless Exception === e
87
+ raise e
88
+ rescue
89
+ raise "wtf?\n#{ buf }\n"
90
+ end
91
+ end
92
+ thread = new_thread cid, @block if @block
93
+ pipe.read rescue nil
94
+ end
95
+ }
96
+ status = $?
97
+ ensure
98
+ if thread
99
+ begin
100
+ class << status
101
+ attr 'thread'
102
+ end
103
+ status.instance_eval{ @thread = thread }
104
+ rescue
105
+ 42
106
+ end
107
+ end
108
+ end
109
+
110
+ if @stdout or @stderr
111
+ open(c['stdout']){|f| relay f => @stdout} if @stdout
112
+ open(c['stderr']){|f| relay f => @stderr} if @stderr
113
+ status
114
+ else
115
+ [status, IO.read(c['stdout']), IO.read(c['stderr'])]
116
+ end
117
+ end
118
+ end
119
+
120
+ def quote *args, &block
121
+ SystemUniversal.quote(*args, &block)
122
+ end
123
+
124
+ def new_thread cid, block
125
+ q = Queue.new
126
+ Thread.new(cid) do |cid|
127
+ current = Thread.current
128
+ current.abort_on_exception = true
129
+ q.push current
130
+ block.call cid
131
+ end
132
+ q.pop
133
+ end
134
+
135
+ def child_setup tmp
136
+ stdin = File.expand_path(File.join(tmp, 'stdin'))
137
+ stdout = File.expand_path(File.join(tmp, 'stdout'))
138
+ stderr = File.expand_path(File.join(tmp, 'stderr'))
139
+ program = File.expand_path(File.join(tmp, 'program'))
140
+ config = File.expand_path(File.join(tmp, 'config'))
141
+
142
+ if @stdin
143
+ open(stdin, 'w'){|f| relay @stdin => f}
144
+ else
145
+ FileUtils.touch stdin
146
+ end
147
+ FileUtils.touch stdout
148
+ FileUtils.touch stderr
149
+
150
+ c = {}
151
+ c['argv'] = @argv
152
+ c['env'] = @env
153
+ c['cwd'] = @cwd
154
+ c['stdin'] = stdin
155
+ c['stdout'] = stdout
156
+ c['stderr'] = stderr
157
+ c['program'] = program
158
+ open(config, 'w'){|f| Marshal.dump(c, f)}
159
+
160
+ open(program, 'w'){|f| f.write child_program(config)}
161
+
162
+ c
163
+ end
164
+
165
+ def quietly
166
+ v = $VERBOSE
167
+ $VERBOSE = nil
168
+ yield
169
+ ensure
170
+ $VERBOSE = v
171
+ end
172
+
173
+ def child_program config
174
+ <<-program
175
+ # encoding: utf-8
176
+
177
+ PIPE = STDOUT.dup
178
+ begin
179
+ config = Marshal.load(IO.read('#{ config }'))
180
+
181
+ argv = config['argv']
182
+ env = config['env']
183
+ cwd = config['cwd']
184
+ stdin = config['stdin']
185
+ stdout = config['stdout']
186
+ stderr = config['stderr']
187
+
188
+ Dir.chdir cwd if cwd
189
+ env.each{|k,v| ENV[k.to_s] = v.to_s} if env
190
+
191
+ STDIN.reopen stdin
192
+ STDOUT.reopen stdout
193
+ STDERR.reopen stderr
194
+
195
+ PIPE.puts "pid: \#{ Process.pid }"
196
+ PIPE.flush ### the process is ready yo!
197
+ PIPE.close
198
+ if RUBY_VERSION >= "1.9"
199
+ exec *argv
200
+ else
201
+ exec argv
202
+ end
203
+ rescue Exception => e
204
+ PIPE.write Marshal.dump(e) rescue nil
205
+ exit 42
206
+ end
207
+ program
208
+ end
209
+
210
+ def relay srcdst
211
+ src, dst, ignored = srcdst.to_a.first
212
+ if src.respond_to? 'read'
213
+ while((buf = src.read(8192))); dst << buf; end
214
+ else
215
+ if src.respond_to?(:each_line)
216
+ src.each_line{|buf| dst << buf}
217
+ else
218
+ src.each{|buf| dst << buf}
219
+ end
220
+ end
221
+ end
222
+
223
+ def tmpdir d = Dir.tmpdir, max = 42, &b
224
+ i = -1 and loop{
225
+ i += 1
226
+
227
+ tmp = File.join d, "systemu_#{ @host }_#{ @ppid }_#{ @pid }_#{ rand }_#{ i += 1 }"
228
+
229
+ begin
230
+ Dir.mkdir tmp
231
+ rescue Errno::EEXIST
232
+ raise if i >= max
233
+ next
234
+ end
235
+
236
+ break(
237
+ if b
238
+ begin
239
+ b.call tmp
240
+ ensure
241
+ FileUtils.rm_rf tmp unless SystemU.turd
242
+ end
243
+ else
244
+ tmp
245
+ end
246
+ )
247
+ }
248
+ end
249
+
250
+ def getopts opts = {}
251
+ lambda do |*args|
252
+ keys, default, ignored = args
253
+ catch(:opt) do
254
+ [keys].flatten.each do |key|
255
+ [key, key.to_s, key.to_s.intern].each do |key|
256
+ throw :opt, opts[key] if opts.has_key?(key)
257
+ end
258
+ end
259
+ default
260
+ end
261
+ end
262
+ end
263
+ end
264
+
265
+ # some monkeypatching for JRuby
266
+ if defined? JRUBY_VERSION
267
+ require 'jruby'
268
+ java_import org.jruby.RubyProcess
269
+
270
+ class SystemUniversal
271
+ def systemu
272
+ split_argv = JRuby::PathHelper.smart_split_command @argv
273
+ process = java.lang.Runtime.runtime.exec split_argv.to_java(:string)
274
+
275
+ stdout, stderr = [process.input_stream, process.error_stream].map do |stream|
276
+ StreamReader.new(stream)
277
+ end
278
+
279
+ exit_code = process.wait_for
280
+ field = process.get_class.get_declared_field("pid")
281
+ field.set_accessible(true)
282
+ pid = field.get(process)
283
+ [
284
+ RubyProcess::RubyStatus.new_process_status(JRuby.runtime, exit_code, pid),
285
+ stdout.join,
286
+ stderr.join
287
+ ]
288
+ end
289
+
290
+ class StreamReader
291
+ def initialize(stream)
292
+ @data = ""
293
+ @thread = Thread.new do
294
+ reader = java.io.BufferedReader.new java.io.InputStreamReader.new(stream)
295
+
296
+ while line = reader.read_line
297
+ @data << line << "\n"
298
+ end
299
+ end
300
+ end
301
+
302
+ def join
303
+ @thread.join
304
+ @data
305
+ end
306
+ end
307
+ end
308
+ end
309
+
310
+
311
+
312
+ SystemU = SystemUniversal unless defined? SystemU
313
+ Systemu = SystemUniversal unless defined? Systemu
314
+
315
+
316
+
317
+
318
+
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+
327
+ if $0 == __FILE__
328
+ #
329
+ # date
330
+ #
331
+ date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
332
+
333
+ status, stdout, stderr = systemu date
334
+ p [status, stdout, stderr]
335
+
336
+ status = systemu date, 1=>(stdout = '')
337
+ p [status, stdout]
338
+
339
+ status = systemu date, 2=>(stderr = '')
340
+ p [status, stderr]
341
+ #
342
+ # sleep
343
+ #
344
+ sleep = %q( ruby -e" p(sleep(1)) " )
345
+ status, stdout, stderr = systemu sleep
346
+ p [status, stdout, stderr]
347
+
348
+ sleep = %q( ruby -e" p(sleep(42)) " )
349
+ status, stdout, stderr = systemu(sleep){|cid| Process.kill 9, cid}
350
+ p [status, stdout, stderr]
351
+ #
352
+ # env
353
+ #
354
+ env = %q( ruby -e" p ENV['A'] " )
355
+ status, stdout, stderr = systemu env, :env => {'A' => 42}
356
+ p [status, stdout, stderr]
357
+ #
358
+ # cwd
359
+ #
360
+ env = %q( ruby -e" p Dir.pwd " )
361
+ status, stdout, stderr = systemu env, :cwd => Dir.tmpdir
362
+ p [status, stdout, stderr]
363
+ end
@@ -0,0 +1,45 @@
1
+ ## systemu.gemspec
2
+ #
3
+
4
+ Gem::Specification::new do |spec|
5
+ spec.name = "systemu"
6
+ spec.version = "2.5.0"
7
+ spec.platform = Gem::Platform::RUBY
8
+ spec.summary = "systemu"
9
+ spec.description = "description: systemu kicks the ass"
10
+
11
+ spec.files =
12
+ ["LICENSE",
13
+ "README",
14
+ "README.erb",
15
+ "Rakefile",
16
+ "lib",
17
+ "lib/systemu.rb",
18
+ "samples",
19
+ "samples/a.rb",
20
+ "samples/b.rb",
21
+ "samples/c.rb",
22
+ "samples/d.rb",
23
+ "samples/e.rb",
24
+ "samples/f.rb",
25
+ "systemu.gemspec",
26
+ "test",
27
+ "test/systemu_test.rb",
28
+ "test/testing.rb"]
29
+
30
+ spec.executables = []
31
+
32
+ spec.require_path = "lib"
33
+
34
+ spec.test_files = nil
35
+
36
+ ### spec.add_dependency 'lib', '>= version'
37
+ #### spec.add_dependency 'map'
38
+
39
+ spec.extensions.push(*[])
40
+
41
+ spec.rubyforge_project = "codeforpeople"
42
+ spec.author = "Ara T. Howard"
43
+ spec.email = "ara.t.howard@gmail.com"
44
+ spec.homepage = "https://github.com/ahoward/systemu"
45
+ end
@@ -12,17 +12,17 @@ describe "Deploying an application without Bundler" do
12
12
 
13
13
  # run a deploy
14
14
  config = EY::Serverside::Deploy::Configuration.new({
15
- "strategy" => "IntegrationSpec",
16
- "deploy_to" => @deploy_dir.to_s,
17
- "group" => `id -gn`.strip,
18
- "stack" => 'nginx_passenger',
19
- "migrate" => nil,
20
- 'app' => 'foo',
21
- 'framework_env' => 'staging'
22
- })
15
+ "strategy" => "IntegrationSpec",
16
+ "deploy_to" => @deploy_dir.to_s,
17
+ "group" => `id -gn`.strip,
18
+ "stack" => 'nginx_passenger',
19
+ "migrate" => nil,
20
+ 'app' => 'foo',
21
+ 'framework_env' => 'staging'
22
+ })
23
23
 
24
24
  @binpath = File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'engineyard-serverside'))
25
- @deployer = FullTestDeploy.new(config, test_shell)
25
+ @deployer = FullTestDeploy.new(config)
26
26
  @deployer.deploy
27
27
  end
28
28
 
@@ -32,7 +32,7 @@ describe "Deploying an application that uses Bundler" do
32
32
  end
33
33
 
34
34
  @binpath = File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'engineyard-serverside'))
35
- @deployer = FullTestDeploy.new(config, test_shell)
35
+ @deployer = FullTestDeploy.new(config)
36
36
  @deployer.deploy
37
37
  end
38
38
 
@@ -11,6 +11,7 @@ describe "the EY::Serverside::Deploy API" do
11
11
  # cheat a bit; we don't actually want to do these things
12
12
  def require_custom_tasks() end
13
13
  def callback(*_) end
14
+ def puts(*_) 'stfu' end
14
15
 
15
16
  attr_reader :call_order
16
17
  def initialize(*a)
@@ -33,7 +34,7 @@ describe "the EY::Serverside::Deploy API" do
33
34
  def disable_maintenance_page() @call_order << 'disable_maintenance_page' end
34
35
  end
35
36
 
36
- td = TestDeploy.new(EY::Serverside::Deploy::Configuration.new, test_shell)
37
+ td = TestDeploy.new(EY::Serverside::Deploy::Configuration.new)
37
38
  td.deploy
38
39
  td.call_order.should == %w(
39
40
  push_code
@@ -51,66 +52,6 @@ describe "the EY::Serverside::Deploy API" do
51
52
  cleanup_old_releases)
52
53
  end
53
54
 
54
- describe "ey.yml loading" do
55
- before(:each) do
56
- @tempdir = `mktemp -d -t ey_yml_spec.XXXXX`.strip
57
- @config = EY::Serverside::Deploy::Configuration.new({
58
- 'repository_cache' => @tempdir,
59
- 'environment_name' => 'env_name',
60
- 'account_name' => 'acc',
61
- 'migrate' => nil,
62
- 'config' => {'branch' => 'branch_from_config'}.to_json
63
- })
64
-
65
- @deploy = FullTestDeploy.new(@config, test_shell)
66
-
67
- @yaml_data = {
68
- 'environments' => {
69
- 'env_name' => {
70
- 'copy_exclude' => ['.git'],
71
- 'migrate' => true,
72
- 'migration_command' => 'uh oh',
73
- 'branch' => 'branch_from_ey_yaml'
74
- }
75
- }
76
- }
77
- end
78
-
79
- def write_ey_yml(relative_path, data)
80
- FileUtils.mkdir_p(File.join(
81
- @tempdir,
82
- File.dirname(relative_path)))
83
-
84
- File.open(File.join(@tempdir, relative_path), 'w') do |f|
85
- f.write data.to_yaml
86
- end
87
- end
88
-
89
- it "requires 'ey.yml' and adds any defined methods to the deploy" do
90
- write_ey_yml 'ey.yml', @yaml_data
91
- @deploy.load_ey_yml
92
- @deploy.config.copy_exclude.should == ['.git']
93
- end
94
-
95
- it "falls back to 'config/ey.yml'" do
96
- write_ey_yml 'config/ey.yml', @yaml_data
97
- @deploy.load_ey_yml
98
- @deploy.config.copy_exclude.should == ['.git']
99
- end
100
-
101
- it "loads at lower priority than command line options" do
102
- write_ey_yml 'ey.yml', @yaml_data
103
- @deploy.load_ey_yml
104
- @deploy.config.migrate?.should == false
105
- end
106
-
107
- it "loads at lower priority than json config option" do
108
- write_ey_yml 'ey.yml', @yaml_data
109
- @deploy.load_ey_yml
110
- @deploy.config.branch.should == 'branch_from_config'
111
- end
112
- end
113
-
114
55
  describe "task overrides" do
115
56
  class TestQuietDeploy < EY::Serverside::Deploy
116
57
  def puts(*_) 'quiet' end
@@ -119,7 +60,7 @@ describe "the EY::Serverside::Deploy API" do
119
60
  before(:each) do
120
61
  @tempdir = `mktemp -d -t custom_deploy_spec.XXXXX`.strip
121
62
  @config = EY::Serverside::Deploy::Configuration.new('repository_cache' => @tempdir)
122
- @deploy = TestQuietDeploy.new(@config, test_shell)
63
+ @deploy = TestQuietDeploy.new(@config)
123
64
  end
124
65
 
125
66
  def write_eydeploy(relative_path, contents = "def got_new_methods() 'from the file on disk' end")
@@ -151,7 +92,7 @@ describe "the EY::Serverside::Deploy API" do
151
92
  def value() 'base' end
152
93
  end
153
94
 
154
- deploy = TestDeploySuper.new(@config, test_shell)
95
+ deploy = TestDeploySuper.new(@config)
155
96
  deploy.require_custom_tasks.should be_true
156
97
  deploy.value.should == "base + derived"
157
98
  end