ngauthier-hydra 0.24.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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.rdoc +51 -0
  6. data/Rakefile +8 -0
  7. data/TODO +18 -0
  8. data/hydra-icon-64x64.png +0 -0
  9. data/hydra.gemspec +24 -0
  10. data/hydra_gray.png +0 -0
  11. data/lib/hydra.rb +16 -0
  12. data/lib/hydra/cucumber/formatter.rb +29 -0
  13. data/lib/hydra/cucumber/partial_html.rb +24 -0
  14. data/lib/hydra/hash.rb +16 -0
  15. data/lib/hydra/js/lint.js +5150 -0
  16. data/lib/hydra/listener/abstract.rb +39 -0
  17. data/lib/hydra/listener/cucumber.css +279 -0
  18. data/lib/hydra/listener/cucumber_html_report.rb +148 -0
  19. data/lib/hydra/listener/jquery-min.js +154 -0
  20. data/lib/hydra/listener/minimal_output.rb +24 -0
  21. data/lib/hydra/listener/notifier.rb +17 -0
  22. data/lib/hydra/listener/progress_bar.rb +48 -0
  23. data/lib/hydra/listener/report_generator.rb +33 -0
  24. data/lib/hydra/master.rb +248 -0
  25. data/lib/hydra/message.rb +47 -0
  26. data/lib/hydra/message/master_messages.rb +19 -0
  27. data/lib/hydra/message/runner_messages.rb +46 -0
  28. data/lib/hydra/message/worker_messages.rb +52 -0
  29. data/lib/hydra/messaging_io.rb +49 -0
  30. data/lib/hydra/pipe.rb +61 -0
  31. data/lib/hydra/runner.rb +312 -0
  32. data/lib/hydra/runner_listener/abstract.rb +23 -0
  33. data/lib/hydra/safe_fork.rb +31 -0
  34. data/lib/hydra/spec/autorun_override.rb +3 -0
  35. data/lib/hydra/spec/hydra_formatter.rb +26 -0
  36. data/lib/hydra/ssh.rb +41 -0
  37. data/lib/hydra/stdio.rb +16 -0
  38. data/lib/hydra/sync.rb +99 -0
  39. data/lib/hydra/tasks.rb +375 -0
  40. data/lib/hydra/tmpdir.rb +11 -0
  41. data/lib/hydra/trace.rb +24 -0
  42. data/lib/hydra/version.rb +3 -0
  43. data/lib/hydra/worker.rb +170 -0
  44. data/test/fixtures/assert_true.rb +7 -0
  45. data/test/fixtures/config.yml +4 -0
  46. data/test/fixtures/conflicting.rb +10 -0
  47. data/test/fixtures/features/step_definitions.rb +21 -0
  48. data/test/fixtures/features/write_alternate_file.feature +7 -0
  49. data/test/fixtures/features/write_file.feature +7 -0
  50. data/test/fixtures/hello_world.rb +3 -0
  51. data/test/fixtures/hydra_worker_init.rb +2 -0
  52. data/test/fixtures/js_file.js +4 -0
  53. data/test/fixtures/json_data.json +4 -0
  54. data/test/fixtures/many_outputs_to_console.rb +9 -0
  55. data/test/fixtures/master_listeners.rb +10 -0
  56. data/test/fixtures/runner_listeners.rb +23 -0
  57. data/test/fixtures/slow.rb +9 -0
  58. data/test/fixtures/sync_test.rb +8 -0
  59. data/test/fixtures/task_test_config.yml +6 -0
  60. data/test/fixtures/write_file.rb +10 -0
  61. data/test/fixtures/write_file_alternate_spec.rb +10 -0
  62. data/test/fixtures/write_file_spec.rb +9 -0
  63. data/test/fixtures/write_file_with_pending_spec.rb +11 -0
  64. data/test/master_test.rb +383 -0
  65. data/test/message_test.rb +31 -0
  66. data/test/pipe_test.rb +38 -0
  67. data/test/runner_test.rb +196 -0
  68. data/test/ssh_test.rb +25 -0
  69. data/test/sync_test.rb +113 -0
  70. data/test/task_test.rb +21 -0
  71. data/test/test_helper.rb +107 -0
  72. data/test/worker_test.rb +60 -0
  73. metadata +229 -0
@@ -0,0 +1,23 @@
1
+ module Hydra #:nodoc:
2
+ module RunnerListener #:nodoc:
3
+ # Abstract listener that implements all the events
4
+ # but does nothing.
5
+ class Abstract
6
+ # Create a new listener.
7
+ #
8
+ # Output: The IO object for outputting any information.
9
+ # Defaults to STDOUT, but you could pass a file in, or STDERR
10
+ def initialize(output = $stdout)
11
+ @output = output
12
+ end
13
+
14
+ # Fired by the runner just before requesting the first file
15
+ def runner_begin( runner )
16
+ end
17
+
18
+ # Fired by the runner just after stoping
19
+ def runner_end( runner )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ class SafeFork
2
+ def self.fork
3
+ begin
4
+ # remove our connection so it doesn't get cloned
5
+ connection = ActiveRecord::Base.remove_connection if defined?(ActiveRecord)
6
+ # fork a process
7
+ child = Process.fork do
8
+ begin
9
+ # create a new connection and perform the action
10
+ begin
11
+ ActiveRecord::Base.establish_connection((connection || {}).merge({:allow_concurrency => true})) if defined?(ActiveRecord)
12
+ rescue ActiveRecord::AdapterNotSpecified
13
+ # AR was defined but we didn't have a connection
14
+ end
15
+ yield
16
+ ensure
17
+ # make sure we remove the connection before we're done
18
+ ActiveRecord::Base.remove_connection if defined?(ActiveRecord)
19
+ end
20
+ end
21
+ ensure
22
+ # make sure we re-establish the connection before returning to the main instance
23
+ begin
24
+ ActiveRecord::Base.establish_connection((connection || {}).merge({:allow_concurrency => true})) if defined?(ActiveRecord)
25
+ rescue ActiveRecord::AdapterNotSpecified
26
+ # AR was defined but we didn't have a connection
27
+ end
28
+ end
29
+ return child
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ if defined?(RSpec)
2
+ RSpec::Core::Runner.disable_autorun!
3
+ end
@@ -0,0 +1,26 @@
1
+ require 'rspec/core/formatters/progress_formatter'
2
+ module RSpec
3
+ module Core
4
+ module Formatters
5
+ class HydraFormatter < ProgressFormatter
6
+ def example_passed(example)
7
+ end
8
+
9
+ def example_pending(example)
10
+ end
11
+
12
+ def example_failed(example)
13
+ end
14
+
15
+ # Stifle the post-test summary
16
+ def dump_summary(duration, example, failure, pending)
17
+ end
18
+
19
+ # Stifle pending specs
20
+ def dump_pending
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
data/lib/hydra/ssh.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'open3'
2
+ require 'hydra/messaging_io'
3
+ module Hydra #:nodoc:
4
+ # Read and write with an ssh connection. For example:
5
+ # @ssh = Hydra::SSH.new(
6
+ # 'localhost', # connect to this machine
7
+ # '/home/user', # move to the home directory
8
+ # "ruby hydra/test/echo_the_dolphin.rb" # run the echo script
9
+ # )
10
+ # @message = Hydra::Messages::TestMessage.new("Hey there!")
11
+ # @ssh.write @message
12
+ # puts @ssh.gets.text
13
+ # => "Hey there!"
14
+ #
15
+ # Note that what ever process you run should respond with Hydra messages.
16
+ class SSH
17
+ include Open3
18
+ include Hydra::MessagingIO
19
+
20
+ # Initialize new SSH connection.
21
+ # The first parameter is passed directly to ssh for starting a connection.
22
+ # The second parameter is the directory to CD into once connected.
23
+ # The third parameter is the command to run
24
+ # So you can do:
25
+ # Hydra::SSH.new('-p 3022 user@server.com', '/home/user/Desktop', 'ls -l')
26
+ # To connect to server.com as user on port 3022, then CD to their desktop, then
27
+ # list all the files.
28
+ def initialize(connection_options, directory, command)
29
+ @writer, @reader, @error = popen3("ssh -tt #{connection_options}")
30
+ @writer.write("mkdir -p #{directory}\n")
31
+ @writer.write("cd #{directory}\n")
32
+ @writer.write(command+"\n")
33
+ end
34
+
35
+ # Close the SSH connection
36
+ def close
37
+ @writer.write "exit\n"
38
+ super
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,16 @@
1
+ require 'hydra/messaging_io'
2
+ module Hydra #:nodoc:
3
+ # Read and write via stdout and stdin.
4
+ class Stdio
5
+ include Hydra::MessagingIO
6
+
7
+ # Initialize new Stdio
8
+ def initialize()
9
+ @reader = $stdin
10
+ @writer = $stdout
11
+ @reader.sync = true
12
+ @writer.sync = true
13
+ end
14
+ end
15
+ end
16
+
data/lib/hydra/sync.rb ADDED
@@ -0,0 +1,99 @@
1
+ require 'yaml'
2
+ module Hydra #:nodoc:
3
+ # Hydra class responsible for delegate work down to workers.
4
+ #
5
+ # The Sync is run once for each remote worker.
6
+ class Sync
7
+ traceable('SYNC')
8
+ self.class.traceable('SYNC MANY')
9
+
10
+ attr_reader :connect, :ssh_opts, :remote_dir
11
+
12
+ # Create a new Sync instance to rsync source from the local machine to a remote worker
13
+ #
14
+ # Arguments:
15
+ # * :worker_opts
16
+ # * A hash of the configuration options for a worker.
17
+ # * :sync
18
+ # * A hash of settings specifically for copying the source directory to be tested
19
+ # to the remote worked
20
+ # * :verbose
21
+ # * Set to true to see lots of Hydra output (for debugging)
22
+ def initialize(worker_opts, sync_opts, verbose = false)
23
+ worker_opts ||= {}
24
+ worker_opts.stringify_keys!
25
+ @verbose = verbose
26
+ @connect = worker_opts.fetch('connect') { raise "You must specify an SSH connection target" }
27
+ @ssh_opts = worker_opts.fetch('ssh_opts') { "" }
28
+ @remote_dir = worker_opts.fetch('directory') { raise "You must specify a remote directory" }
29
+
30
+ return unless sync_opts
31
+ sync_opts.stringify_keys!
32
+ @local_dir = sync_opts.fetch('directory') { raise "You must specify a synchronization directory" }
33
+ @exclude_paths = sync_opts.fetch('exclude') { [] }
34
+
35
+ trace "Initialized"
36
+ trace " Worker: (#{worker_opts.inspect})"
37
+ trace " Sync: (#{sync_opts.inspect})"
38
+
39
+ sync
40
+ end
41
+
42
+ def sync
43
+ #trace "Synchronizing with #{connect}\n\t#{sync_opts.inspect}"
44
+ exclude_opts = @exclude_paths.inject(''){|memo, path| memo += "--exclude=#{path} "}
45
+
46
+ rsync_command = [
47
+ 'rsync',
48
+ '-avz',
49
+ '--delete',
50
+ exclude_opts,
51
+ File.expand_path(@local_dir)+'/',
52
+ "-e \"ssh #{@ssh_opts}\"",
53
+ "#{@connect}:#{@remote_dir}"
54
+ ].join(" ")
55
+ trace rsync_command
56
+ trace `#{rsync_command}`
57
+ end
58
+
59
+ def self.sync_many opts
60
+ opts.stringify_keys!
61
+ config_file = opts.delete('config') { nil }
62
+ if config_file
63
+ opts.merge!(YAML.load_file(config_file).stringify_keys!)
64
+ end
65
+ @verbose = opts.fetch('verbose') { false }
66
+ @sync = opts.fetch('sync') { {} }
67
+
68
+ workers_opts = opts.fetch('workers') { [] }
69
+ @remote_worker_opts = []
70
+ workers_opts.each do |worker_opts|
71
+ worker_opts.stringify_keys!
72
+ if worker_opts['type'].to_s == 'ssh'
73
+ @remote_worker_opts << worker_opts
74
+ end
75
+ end
76
+
77
+ trace "Initialized"
78
+ trace " Sync: (#{@sync.inspect})"
79
+ trace " Workers: (#{@remote_worker_opts.inspect})"
80
+
81
+ Thread.abort_on_exception = true
82
+ trace "Processing workers"
83
+ @listeners = []
84
+ @remote_worker_opts.each do |worker_opts|
85
+ @listeners << Thread.new do
86
+ begin
87
+ trace "Syncing #{worker_opts.inspect}"
88
+ Sync.new worker_opts, @sync, @verbose
89
+ rescue
90
+ trace "Syncing failed [#{worker_opts.inspect}]"
91
+ end
92
+ end
93
+ end
94
+
95
+ @listeners.each{|l| l.join}
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,375 @@
1
+ require 'open3'
2
+ module Hydra #:nodoc:
3
+ # Hydra Task Common attributes and methods
4
+ class Task
5
+ include Rake::DSL if defined?(Rake::DSL)
6
+
7
+ # Name of the task. Default 'hydra'
8
+ attr_accessor :name
9
+
10
+ # Command line options
11
+ attr_accessor :options
12
+
13
+ # Files to test.
14
+ # You can add files manually via:
15
+ # t.files << [file1, file2, etc]
16
+ #
17
+ # Or you can use the add_files method
18
+ attr_accessor :files
19
+
20
+ # True if you want to see Hydra's message traces
21
+ attr_accessor :verbose
22
+
23
+ # Path to the hydra config file.
24
+ # If not set, it will check 'hydra.yml' and 'config/hydra.yml'
25
+ attr_accessor :config
26
+
27
+ # Automatically sort files using their historical runtimes.
28
+ # Defaults to true
29
+ # To disable:
30
+ # t.autosort = false
31
+ attr_accessor :autosort
32
+
33
+ # Event listeners. Defaults to the MinimalOutput listener.
34
+ # You can add additional listeners if you'd like. For example,
35
+ # on linux (with notify-send) you can add the notifier listener:
36
+ # t.listeners << Hydra::Listener::Notifier.new
37
+ attr_accessor :listeners
38
+
39
+ # Set to true if you want to run this task only on the local
40
+ # machine with one runner. A "Safe Mode" for some test
41
+ # files that may not play nice with others.
42
+ attr_accessor :serial
43
+
44
+ attr_accessor :environment
45
+
46
+ # Set to false if you don't want to show the total running time
47
+ attr_accessor :show_time
48
+
49
+ # Set to a valid file path if you want to save the output of the runners
50
+ # in a log file
51
+ attr_accessor :runner_log_file
52
+
53
+ #
54
+ # Search for the hydra config file
55
+ def find_config_file
56
+ @config ||= 'hydra.yml'
57
+ return @config if File.exists?(@config)
58
+ @config = File.join('config', 'hydra.yml')
59
+ return @config if File.exists?(@config)
60
+ @config = nil
61
+ end
62
+
63
+ # Add files to test by passing in a string to be run through Dir.glob.
64
+ # For example:
65
+ #
66
+ # t.add_files 'test/units/*.rb'
67
+ def add_files(pattern)
68
+ @files += Dir.glob(pattern)
69
+ end
70
+
71
+ end
72
+
73
+ # Define a test task that uses hydra to test the files.
74
+ #
75
+ # Hydra::TestTask.new('hydra') do |t|
76
+ # t.add_files 'test/unit/**/*_test.rb'
77
+ # t.add_files 'test/functional/**/*_test.rb'
78
+ # t.add_files 'test/integration/**/*_test.rb'
79
+ # t.verbose = false # optionally set to true for lots of debug messages
80
+ # t.autosort = false # disable automatic sorting based on runtime of tests
81
+ # end
82
+ class TestTask < Hydra::Task
83
+
84
+ # Create a new HydraTestTask
85
+ def initialize(name = :hydra)
86
+ @name = name
87
+ @files = []
88
+ @verbose = false
89
+ @autosort = true
90
+ @serial = false
91
+ @listeners = [Hydra::Listener::ProgressBar.new]
92
+ @show_time = true
93
+ @options = ''
94
+
95
+ yield self if block_given?
96
+
97
+ # Ensure we override rspec's at_exit
98
+ if defined?(RSpec)
99
+ RSpec::Core::Runner.disable_autorun!
100
+ end
101
+
102
+ unless @serial
103
+ @config = find_config_file
104
+ end
105
+
106
+ @opts = {
107
+ :verbose => @verbose,
108
+ :autosort => @autosort,
109
+ :files => @files,
110
+ :listeners => @listeners,
111
+ :environment => @environment,
112
+ :runner_log_file => @runner_log_file,
113
+ :options => @options
114
+ }
115
+ if @config
116
+ @opts.merge!(:config => @config)
117
+ else
118
+ @opts.merge!(:workers => [{:type => :local, :runners => 1}])
119
+ end
120
+
121
+ define
122
+ end
123
+
124
+ private
125
+ # Create the rake task defined by this HydraTestTask
126
+ def define
127
+ desc "Hydra Tests" + (@name == :hydra ? "" : " for #{@name}")
128
+ task @name do
129
+ if Object.const_defined?('Rails') && Rails.env == 'development'
130
+ $stderr.puts %{WARNING: Rails Environment is "development". Make sure to set it properly (ex: "RAILS_ENV=test rake hydra")}
131
+ end
132
+
133
+ start = Time.now if @show_time
134
+
135
+ puts '********************'
136
+ puts @options.inspect
137
+ master = Hydra::Master.new(@opts)
138
+
139
+ $stdout.puts "\nFinished in #{'%.6f' % (Time.now - start)} seconds." if @show_time
140
+
141
+ unless master.failed_files.empty?
142
+ raise "Hydra: Not all tests passes"
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ # Define a test task that uses hydra to profile your test files
149
+ #
150
+ # Hydra::ProfileTask.new('hydra:prof') do |t|
151
+ # t.add_files 'test/unit/**/*_test.rb'
152
+ # t.add_files 'test/functional/**/*_test.rb'
153
+ # t.add_files 'test/integration/**/*_test.rb'
154
+ # t.generate_html = true # defaults to false
155
+ # t.generate_text = true # defaults to true
156
+ # end
157
+ class ProfileTask < Hydra::Task
158
+ # boolean: generate html output from ruby-prof
159
+ attr_accessor :generate_html
160
+ # boolean: generate text output from ruby-prof
161
+ attr_accessor :generate_text
162
+
163
+ # Create a new Hydra ProfileTask
164
+ def initialize(name = 'hydra:profile')
165
+ @name = name
166
+ @files = []
167
+ @verbose = false
168
+ @generate_html = false
169
+ @generate_text = true
170
+
171
+ yield self if block_given?
172
+
173
+ # Ensure we override rspec's at_exit
174
+ require 'hydra/spec/autorun_override'
175
+
176
+ @config = find_config_file
177
+
178
+ @opts = {
179
+ :verbose => @verbose,
180
+ :files => @files
181
+ }
182
+ define
183
+ end
184
+
185
+ private
186
+ # Create the rake task defined by this HydraTestTask
187
+ def define
188
+ desc "Hydra Test Profile" + (@name == :hydra ? "" : " for #{@name}")
189
+ task @name do
190
+ require 'ruby-prof'
191
+ RubyProf.start
192
+
193
+ runner = Hydra::Runner.new(:io => File.new('/dev/null', 'w'))
194
+ @files.each do |file|
195
+ $stdout.write runner.run_file(file)
196
+ $stdout.flush
197
+ end
198
+
199
+ $stdout.write "\nTests complete. Generating profiling output\n"
200
+ $stdout.flush
201
+
202
+ result = RubyProf.stop
203
+
204
+ if @generate_html
205
+ printer = RubyProf::GraphHtmlPrinter.new(result)
206
+ out = File.new("ruby-prof.html", 'w')
207
+ printer.print(out, :min_self => 0.05)
208
+ out.close
209
+ $stdout.write "Profiling data written to [ruby-prof.html]\n"
210
+ end
211
+
212
+ if @generate_text
213
+ printer = RubyProf::FlatPrinter.new(result)
214
+ out = File.new("ruby-prof.txt", 'w')
215
+ printer.print(out, :min_self => 0.05)
216
+ out.close
217
+ $stdout.write "Profiling data written to [ruby-prof.txt]\n"
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ # Define a sync task that uses hydra to rsync the source tree under test to remote workers.
224
+ #
225
+ # This task is very useful to run before a remote db:reset task to make sure the db/schema.rb
226
+ # file is up to date on the remote workers.
227
+ #
228
+ # Hydra::SyncTask.new('hydra:sync') do |t|
229
+ # t.verbose = false # optionally set to true for lots of debug messages
230
+ # end
231
+ class SyncTask < Hydra::Task
232
+
233
+ # Create a new SyncTestTask
234
+ def initialize(name = :sync)
235
+ @name = name
236
+ @verbose = false
237
+
238
+ yield self if block_given?
239
+
240
+ @config = find_config_file
241
+
242
+ @opts = {
243
+ :verbose => @verbose
244
+ }
245
+ @opts.merge!(:config => @config) if @config
246
+
247
+ define
248
+ end
249
+
250
+ private
251
+ # Create the rake task defined by this HydraSyncTask
252
+ def define
253
+ desc "Hydra Tests" + (@name == :hydra ? "" : " for #{@name}")
254
+ task @name do
255
+ Hydra::Sync.sync_many(@opts)
256
+ end
257
+ end
258
+ end
259
+
260
+ # Setup a task that will be run across all remote workers
261
+ # Hydra::RemoteTask.new('db:reset')
262
+ #
263
+ # Then you can run:
264
+ # rake hydra:remote:db:reset
265
+ class RemoteTask < Hydra::Task
266
+ include Open3
267
+ # Create a new hydra remote task with the given name.
268
+ # The task will be named hydra:remote:<name>
269
+ def initialize(name, command=nil)
270
+ @name = name
271
+ @command = command
272
+ yield self if block_given?
273
+ @config = find_config_file
274
+ if @config
275
+ define
276
+ else
277
+ task "hydra:remote:#{@name}" do ; end
278
+ end
279
+ end
280
+
281
+ private
282
+ def define
283
+ desc "Run #{@name} remotely on all workers"
284
+ task "hydra:remote:#{@name}" do
285
+ config = YAML.load_file(@config)
286
+ environment = config.fetch('environment') { 'test' }
287
+ workers = config.fetch('workers') { [] }
288
+ workers = workers.select{|w| w['type'] == 'ssh'}
289
+ @command = "RAILS_ENV=#{environment} rake #{@name}" unless @command
290
+
291
+ $stdout.write "==== Hydra Running #{@name} ====\n"
292
+ Thread.abort_on_exception = true
293
+ @listeners = []
294
+ @results = {}
295
+ workers.each do |worker|
296
+ @listeners << Thread.new do
297
+ begin
298
+ @results[worker] = if run_command(worker, @command)
299
+ "==== #{@name} passed on #{worker['connect']} ====\n"
300
+ else
301
+ "==== #{@name} failed on #{worker['connect']} ====\nPlease see above for more details.\n"
302
+ end
303
+ rescue
304
+ @results[worker] = "==== #{@name} failed for #{worker['connect']} ====\n#{$!.inspect}\n#{$!.backtrace.join("\n")}"
305
+ end
306
+ end
307
+ end
308
+ @listeners.each{|l| l.join}
309
+ $stdout.write "\n==== Hydra Running #{@name} COMPLETE ====\n\n"
310
+ $stdout.write @results.values.join("\n")
311
+ end
312
+ end
313
+
314
+ def run_command worker, command
315
+ $stdout.write "==== Hydra Running #{@name} on #{worker['connect']} ====\n"
316
+ ssh_opts = worker.fetch('ssh_opts') { '' }
317
+ writer, reader, error = popen3("ssh -tt #{ssh_opts} #{worker['connect']} ")
318
+ writer.write("cd #{worker['directory']}\n")
319
+ writer.write "echo BEGIN HYDRA\n"
320
+ writer.write(command + "\r")
321
+ writer.write "echo END HYDRA\n"
322
+ writer.write("exit\n")
323
+ writer.close
324
+ ignoring = true
325
+ passed = true
326
+ while line = reader.gets
327
+ line.chomp!
328
+ if line =~ /^rake aborted!$/
329
+ passed = false
330
+ end
331
+ if line =~ /echo END HYDRA$/
332
+ ignoring = true
333
+ end
334
+ $stdout.write "#{worker['connect']}: #{line}\n" unless ignoring
335
+ if line == 'BEGIN HYDRA'
336
+ ignoring = false
337
+ end
338
+ end
339
+ passed
340
+ end
341
+ end
342
+
343
+ # A Hydra global task is a task that is run both locally and remotely.
344
+ #
345
+ # For example:
346
+ #
347
+ # Hydra::GlobalTask.new('db:reset')
348
+ #
349
+ # Allows you to run:
350
+ #
351
+ # rake hydra:db:reset
352
+ #
353
+ # Then, db:reset will be run locally and on all remote workers. This
354
+ # makes it easy to setup your workers and run tasks all in a row.
355
+ #
356
+ # For example:
357
+ #
358
+ # rake hydra:db:reset hydra:factories hydra:tests
359
+ #
360
+ # Assuming you setup hydra:db:reset and hydra:db:factories as global
361
+ # tasks and hydra:tests as a Hydra::TestTask for all your tests
362
+ class GlobalTask < Hydra::Task
363
+ def initialize(name)
364
+ @name = name
365
+ define
366
+ end
367
+
368
+ private
369
+ def define
370
+ Hydra::RemoteTask.new(@name)
371
+ desc "Run #{@name.to_s} Locally and Remotely across all Workers"
372
+ task "hydra:#{@name.to_s}" => [@name.to_s, "hydra:remote:#{@name.to_s}"]
373
+ end
374
+ end
375
+ end