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 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
@@ -0,0 +1,6 @@
1
+ module FastRake
2
+ VERSION_MAJOR = 0
3
+ VERSION_MINOR = 0
4
+ VERSION_PATCH = 1
5
+ VERSION = [VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH].join('.')
6
+ 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
+