ngauthier-hydra 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
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