fast_rake 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +49 -0
- data/lib/fast_rake/fast_runner.rb +172 -0
- data/lib/fast_rake/version.rb +6 -0
- data/lib/fast_rake.rb +27 -0
- metadata +89 -0
data/README.rdoc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
= fast_rake
|
2
|
+
|
3
|
+
* TODO url
|
4
|
+
|
5
|
+
== SYNOPSIS
|
6
|
+
|
7
|
+
TODO description
|
8
|
+
|
9
|
+
=== Usage
|
10
|
+
|
11
|
+
TODO (code sample of usage)
|
12
|
+
|
13
|
+
== REQUIREMENTS
|
14
|
+
|
15
|
+
* TODO (list of requirements)
|
16
|
+
|
17
|
+
== INSTALLATION
|
18
|
+
|
19
|
+
sudo gem install fast_rake
|
20
|
+
|
21
|
+
== DEVELOPMENT
|
22
|
+
|
23
|
+
TODO developer advice
|
24
|
+
|
25
|
+
== LICENSE
|
26
|
+
|
27
|
+
(The MIT License)
|
28
|
+
|
29
|
+
Copyright (c) 2012 TODO
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
32
|
+
a copy of this software and associated documentation files (the
|
33
|
+
'Software'), to deal in the Software without restriction, including
|
34
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
35
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
36
|
+
permit persons to whom the Software is furnished to do so, subject to
|
37
|
+
the following conditions:
|
38
|
+
|
39
|
+
The above copyright notice and this permission notice shall be
|
40
|
+
included in all copies or substantial portions of the Software.
|
41
|
+
|
42
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
43
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
44
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
45
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
46
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
47
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
48
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
49
|
+
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require "rake"
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
class FastRake::FastRunner
|
5
|
+
|
6
|
+
GREEN = "\033[32m"
|
7
|
+
RED = "\033[31m"
|
8
|
+
YELLOW = "\e[33m"
|
9
|
+
RESET = "\033[0m"
|
10
|
+
|
11
|
+
def initialize(tasks, process_count)
|
12
|
+
@tasks = tasks
|
13
|
+
@process_count = process_count
|
14
|
+
@children = {}
|
15
|
+
@parent = true
|
16
|
+
@env_number = 0
|
17
|
+
@failed=false
|
18
|
+
#put_w_time "Parent PID is: #{Process.pid}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
clean_previous_results
|
23
|
+
check_if_previous_processes_really_finished
|
24
|
+
@start = Time.now
|
25
|
+
|
26
|
+
put_w_time %{Started at #{Time.now.strftime("%H:%M:%S")}}
|
27
|
+
at_exit { kill_remaining_children }
|
28
|
+
|
29
|
+
start_some_children
|
30
|
+
|
31
|
+
wait_for_tasks_to_finish
|
32
|
+
put_w_time "#{@failed ? RED : GREEN}Elapsed time: #{distance_of_time_to_now(@start)}#{RESET}"
|
33
|
+
raise 'failed fast' if @failed
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def check_if_previous_processes_really_finished
|
39
|
+
process = `ps aux | grep -E "ruby|selenium" | grep -vE "grep|foreman|script\/worker|guard|memcached|growl_notify_buildlight|gem server|#{Process.pid}"`
|
40
|
+
lines = process.split("\n")
|
41
|
+
if lines.length > 0
|
42
|
+
puts lines
|
43
|
+
prompt = "#{YELLOW}These ruby|selenium processes were found to be running. Do you want to continue (Y)? (ctrl-c to stop)#{RESET}"
|
44
|
+
puts prompt
|
45
|
+
STDIN.each_line do |line|
|
46
|
+
return if line.downcase.chomp == 'y'
|
47
|
+
puts prompt
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def results_folder
|
53
|
+
Rails.root.join('tmp', 'build')
|
54
|
+
end
|
55
|
+
|
56
|
+
def clean_previous_results
|
57
|
+
`rm -rf "#{results_folder}"`
|
58
|
+
end
|
59
|
+
|
60
|
+
def distance_of_time_to_now(time)
|
61
|
+
seconds = Time.now - time
|
62
|
+
total_minutes = (seconds / 1.minutes).floor
|
63
|
+
seconds_in_last_minute = (seconds - total_minutes.minutes.seconds).floor
|
64
|
+
"%02dm %02ds" % [total_minutes, seconds_in_last_minute]
|
65
|
+
end
|
66
|
+
|
67
|
+
def kill_remaining_children
|
68
|
+
return unless @parent
|
69
|
+
|
70
|
+
@children.each do |pid, task|
|
71
|
+
begin
|
72
|
+
put_w_time "Sending SIGINT to #{pid} (#{task[:name]})"
|
73
|
+
Process.kill("INT", pid)
|
74
|
+
Process.kill("INT", pid) # Twice - cbr / rspec ignore the first one for some reason...
|
75
|
+
rescue Exception
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@children.each do |pid, task|
|
80
|
+
put_w_time "Waiting for #{pid} (#{task[:name]})"
|
81
|
+
wait_for_task_with_timeout(pid)
|
82
|
+
end
|
83
|
+
@children={}
|
84
|
+
end
|
85
|
+
|
86
|
+
def put_w_time(thing)
|
87
|
+
puts %{[#{distance_of_time_to_now(@start)}] #{thing}}
|
88
|
+
end
|
89
|
+
|
90
|
+
def run_in_new_env(task_name, env_number)
|
91
|
+
@parent = false
|
92
|
+
ENV['TEST_ENV_NUMBER'] = env_number.to_s
|
93
|
+
|
94
|
+
output_path = results_folder.join(task_string(task_name))
|
95
|
+
`mkdir -p #{output_path}`
|
96
|
+
STDOUT.reopen(output_path.join('stdout'), 'w')
|
97
|
+
STDERR.reopen(output_path.join('stderr'), 'w')
|
98
|
+
|
99
|
+
setup_database(task_name)
|
100
|
+
Rake::Task[task_name].invoke
|
101
|
+
end
|
102
|
+
|
103
|
+
def setup_database(task_name)
|
104
|
+
ENV["ATLAS_TEST_DB_NAME"] = "atlas_test_#{task_string(task_name)}"
|
105
|
+
Rake::Task["db:create"].reenable
|
106
|
+
Rake::Task["db:create"].invoke
|
107
|
+
Rake::Task["db:test:prepare"].reenable
|
108
|
+
Rake::Task["db:test:prepare"].invoke
|
109
|
+
end
|
110
|
+
|
111
|
+
def start_some_children
|
112
|
+
while @children.length < @process_count && @tasks.length > 0
|
113
|
+
task_name = @tasks.shift
|
114
|
+
if !task_name.nil?
|
115
|
+
@env_number += 1
|
116
|
+
pid = Process.fork { @parent=false; sleep(@env_number / 10); run_in_new_env(task_name, @env_number) }
|
117
|
+
put_w_time "[#{task_name}] started (pid #{pid}); #{@tasks.length} jobs left to start."
|
118
|
+
@children[pid] = {:name => task_name, :start => Time.now}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def task_string(task_name)
|
124
|
+
task_name.to_s.gsub(':', '_')
|
125
|
+
end
|
126
|
+
|
127
|
+
def puts_still_running
|
128
|
+
return if @children.length == 0
|
129
|
+
put_w_time "#{YELLOW}Still running: #{@children.values.collect{|v|v[:name]}.join(' ')}#{RESET}"
|
130
|
+
put_w_time "#{YELLOW}Remaining: #{@tasks.join(' ')}#{RESET}"
|
131
|
+
end
|
132
|
+
|
133
|
+
def wait_for_task_with_timeout(pid, timeout=5)
|
134
|
+
begin
|
135
|
+
Timeout::timeout(timeout) {
|
136
|
+
Process.wait(pid) rescue nil
|
137
|
+
}
|
138
|
+
rescue Timeout::Error
|
139
|
+
begin
|
140
|
+
Process.kill("KILL",pid)
|
141
|
+
rescue
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def wait_for_tasks_to_finish
|
147
|
+
begin
|
148
|
+
while true do # When there are no more child processes wait2 raises Errno::ECHILD
|
149
|
+
pid, status = Process.wait2
|
150
|
+
task = @children.delete(pid)
|
151
|
+
next if task.nil?
|
152
|
+
output_path = results_folder.join(task_string(task[:name]))
|
153
|
+
if status.success?
|
154
|
+
put_w_time "#{GREEN}[#{task[:name]}] finished. Elapsed time was #{distance_of_time_to_now(task[:start])}.#{RESET}"
|
155
|
+
put_w_time "#{GREEN}[#{task[:name]}] Output is in #{output_path}#{RESET}"
|
156
|
+
puts_still_running
|
157
|
+
start_some_children
|
158
|
+
else
|
159
|
+
if !@failed
|
160
|
+
put_w_time "#{RED}[#{task[:name]}] Build failed. Output can be found in #{output_path}#{RESET}"
|
161
|
+
puts_still_running
|
162
|
+
@failed=true
|
163
|
+
kill_remaining_children
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
rescue Errno::ECHILD
|
168
|
+
# Errno::ECHILD indicates you have no child process to wait on.
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
data/lib/fast_rake.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'fast_rake/version'
|
2
|
+
require 'fast_rake/fast_runner'
|
3
|
+
|
4
|
+
module FastRake
|
5
|
+
|
6
|
+
def self.fast_runner(setup_tasks, run_tasks)
|
7
|
+
fast_runner_task(:two, 2, setup_tasks, run_tasks)
|
8
|
+
fast_runner_task(:four, 4, setup_tasks, run_tasks)
|
9
|
+
fast_runner_task(:eight, 8, setup_tasks, run_tasks)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.fast_runner_task(name, processes, setup_tasks, run_tasks)
|
13
|
+
|
14
|
+
desc "Fast test runner for #{processes} cpus"
|
15
|
+
task name, [:list] => setup_tasks do |t, args|
|
16
|
+
tasks_to_run = if !args[:list].nil?
|
17
|
+
args[:list].split(' ')
|
18
|
+
else
|
19
|
+
run_tasks
|
20
|
+
end
|
21
|
+
#puts %{\n\e[33mTo rerun: ber "fast:#{name}[#{tasks_to_run.join(' ')}]"\033[0m\n\n}
|
22
|
+
FastRunner.new(tasks_to_run, processes).run
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fast_rake
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jonathan Ricketson
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-03-15 00:00:00 +11:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: gemma
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 11
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 1
|
33
|
+
- 0
|
34
|
+
version: 2.1.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
description: "\n Runs a small number of setup tasks, followed by a bunch of expensive tasks in parallel.\n This manages the number of running tasks and keeps the visible output to a small and useful amount.\n "
|
38
|
+
email:
|
39
|
+
- jonathan.ricketson@lonelyplanet.com.au
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files:
|
45
|
+
- README.rdoc
|
46
|
+
files:
|
47
|
+
- lib/fast_rake/version.rb
|
48
|
+
- lib/fast_rake/fast_runner.rb
|
49
|
+
- lib/fast_rake.rb
|
50
|
+
- README.rdoc
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: ""
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options:
|
57
|
+
- --main
|
58
|
+
- README.rdoc
|
59
|
+
- --title
|
60
|
+
- fast_rake-0.0.1 Documentation
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
hash: 3
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
version: "0"
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 3
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
requirements: []
|
82
|
+
|
83
|
+
rubyforge_project: fast_rake
|
84
|
+
rubygems_version: 1.5.3
|
85
|
+
signing_key:
|
86
|
+
specification_version: 3
|
87
|
+
summary: Runs a small number of setup tasks, followed by a bunch of expensive tasks in parallel.
|
88
|
+
test_files: []
|
89
|
+
|