fast_rake 0.0.1

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.
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
+