fasten 0.7.6 → 0.8.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +3 -3
- data/lib/fasten/defaults.rb +2 -2
- data/lib/fasten/runner.rb +20 -21
- data/lib/fasten/support/stats.rb +2 -2
- data/lib/fasten/task_manager.rb +32 -4
- data/lib/fasten/ui/console.rb +3 -3
- data/lib/fasten/ui/curses.rb +15 -15
- data/lib/fasten/version.rb +1 -1
- data/lib/fasten.rb +10 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c77fcc3eddc4f86d6b6a2ad36fbeb6f7edb302c6068b65df3dd61d2531e8251a
|
4
|
+
data.tar.gz: 3638c6cf10144c9a47bedfca7ada81547c3f2b15a440a81b1c4b8a055bd38d3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b0ca6dcec14736e254d66ce33f536f70982923d638403f7e3a4cb0c950e59e74727fb47d847d24a8f9a1a80ad594d0cbae9734ef3ef9bdd7e5cc6b12b913ea5
|
7
|
+
data.tar.gz: b1e139f7a7f62677a581a47ca95959919430a13d338e0adf3731d13d2675cbbfeba9a3173d31a5659002da3635a254f6b59cb95060042d77a54cb2faf4359537
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -21,7 +21,7 @@ It has been released so people can try it and make feature requests, comments an
|
|
21
21
|
### Execution:
|
22
22
|
- [x] tasks are executed in parallel, using worker processes/threads
|
23
23
|
- [x] process are preferred on POSIX systems, otherwise threads are used
|
24
|
-
- [x] default number of
|
24
|
+
- [x] default number of jobs is the # of real cpu cores on the system
|
25
25
|
- [x] in case of task Error or Exception, the whole run will be stopped
|
26
26
|
- [x] runner saves a log file in `$(pwd)/fasten/log/runner/*name_of_runner*.log`
|
27
27
|
- [x] each task saves its STDERR/STDOUT to a log file in `$(pwd)/fasten/log/task/*name_of_task*.log`
|
@@ -30,14 +30,14 @@ It has been released so people can try it and make feature requests, comments an
|
|
30
30
|
|
31
31
|
### UI/Curses
|
32
32
|
- [x] only available if the 'curses' gem is installed
|
33
|
-
- [x] Display number of
|
33
|
+
- [x] Display number of jobs running, idle, waiting, and number of jobs (max)
|
34
34
|
- [x] Display current running tasks, pending tasks, waiting tasks, etc; showing which task is pending because other tasks need to be run first
|
35
35
|
- [x] Display the number of tasks done/pending
|
36
36
|
- [x] Display a progressbar
|
37
37
|
- [x] Display current local time
|
38
38
|
- [x] Pause whole runner with `P` key (waits current running tasks end)
|
39
39
|
- [x] Resume paused runner with `R` key
|
40
|
-
- [x] Press ⬅️ or ➡️ keys to dynamically increase/decrease number of
|
40
|
+
- [x] Press ⬅️ or ➡️ keys to dynamically increase/decrease number of jobs
|
41
41
|
- [ ] Use ⬆️ and ⬇️ to select tasks
|
42
42
|
- [ ] Calculate ETA, assuming all tasks take same time
|
43
43
|
- [ ] Calculate ETA, based on saved tasks statistics
|
data/lib/fasten/defaults.rb
CHANGED
@@ -14,7 +14,7 @@ module Fasten
|
|
14
14
|
false
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
17
|
+
def default_jobs
|
18
18
|
Parallel.physical_processor_count
|
19
19
|
end
|
20
20
|
|
@@ -35,7 +35,7 @@ module Fasten
|
|
35
35
|
|
36
36
|
require 'fasten/ui/curses'
|
37
37
|
|
38
|
-
@default_ui_mode = :curses
|
38
|
+
@default_ui_mode = STDIN.tty? && STDOUT.tty? ? :curses : :console
|
39
39
|
rescue StandardError, LoadError
|
40
40
|
@default_ui_mode = :console
|
41
41
|
end
|
data/lib/fasten/runner.rb
CHANGED
@@ -18,27 +18,26 @@ module Fasten
|
|
18
18
|
include Fasten::Support::UI
|
19
19
|
include Fasten::Support::Yaml
|
20
20
|
|
21
|
-
attr_accessor :name, :stats, :summary, :
|
21
|
+
attr_accessor :name, :stats, :summary, :jobs, :worker_class, :fasten_dir, :use_threads, :ui_mode, :developer, :workers, :queue, :tasks
|
22
22
|
|
23
23
|
def initialize(**options)
|
24
|
-
%i[name stats summary
|
24
|
+
%i[name stats summary jobs worker_class fasten_dir use_threads ui_mode developer].each do |key|
|
25
25
|
options[key] = Fasten.send "default_#{key}" unless options.key? key
|
26
26
|
end
|
27
27
|
|
28
|
-
@tasks = TaskManager.new
|
28
|
+
@tasks = TaskManager.new(targets: options[:targets] || [])
|
29
|
+
@workers = []
|
29
30
|
|
30
31
|
reconfigure(options)
|
31
32
|
end
|
32
33
|
|
33
34
|
def reconfigure(**options)
|
34
|
-
%i[name stats summary
|
35
|
+
%i[name stats summary jobs worker_class fasten_dir use_threads ui_mode developer].each do |key|
|
35
36
|
send "#{key}=", options[key] if options.key? key
|
36
37
|
end
|
37
38
|
|
38
39
|
initialize_stats
|
39
40
|
initialize_logger
|
40
|
-
|
41
|
-
self.worker_list ||= []
|
42
41
|
end
|
43
42
|
|
44
43
|
def task(name, **opts, &block)
|
@@ -117,7 +116,7 @@ module Fasten
|
|
117
116
|
end
|
118
117
|
|
119
118
|
def should_wait_for_running_tasks?
|
120
|
-
tasks.running? && (tasks.no_waiting? || tasks.failed? || %i[PAUSING QUITTING].include?(state)) || tasks.running.count >=
|
119
|
+
tasks.running? && (tasks.no_waiting? || tasks.failed? || %i[PAUSING QUITTING].include?(state)) || tasks.running.count >= jobs
|
121
120
|
end
|
122
121
|
|
123
122
|
def wait_for_running_tasks
|
@@ -130,13 +129,13 @@ module Fasten
|
|
130
129
|
while should_wait_for_running_tasks?
|
131
130
|
ui.update
|
132
131
|
|
133
|
-
|
132
|
+
receive_jobs_tasks_thread queue.receive_with_timeout(0.5)
|
134
133
|
end
|
135
134
|
|
136
135
|
ui.update
|
137
136
|
end
|
138
137
|
|
139
|
-
def
|
138
|
+
def receive_jobs_tasks_thread(items)
|
140
139
|
items&.each do |task|
|
141
140
|
tasks.running.delete task
|
142
141
|
|
@@ -153,18 +152,18 @@ module Fasten
|
|
153
152
|
def wait_for_running_tasks_fork
|
154
153
|
while should_wait_for_running_tasks?
|
155
154
|
ui.update
|
156
|
-
reads =
|
155
|
+
reads = workers.map(&:parent_read)
|
157
156
|
reads, _writes, _errors = IO.select(reads, [], [], 0.5)
|
158
157
|
|
159
|
-
|
158
|
+
receive_jobs_tasks_fork(reads)
|
160
159
|
end
|
161
160
|
|
162
161
|
ui.update
|
163
162
|
end
|
164
163
|
|
165
|
-
def
|
164
|
+
def receive_jobs_tasks_fork(reads)
|
166
165
|
reads&.each do |read|
|
167
|
-
next unless (worker =
|
166
|
+
next unless (worker = workers.find { |item| item.parent_read == read })
|
168
167
|
|
169
168
|
task = worker.receive_response_from_child
|
170
169
|
|
@@ -206,24 +205,24 @@ module Fasten
|
|
206
205
|
end
|
207
206
|
|
208
207
|
def remove_workers_as_needed
|
209
|
-
while
|
210
|
-
return unless (worker =
|
208
|
+
while workers.count > jobs
|
209
|
+
return unless (worker = workers.find { |item| item.running_task.nil? })
|
211
210
|
|
212
211
|
worker.kill
|
213
|
-
|
212
|
+
workers.delete worker
|
214
213
|
|
215
214
|
ui.force_clear
|
216
215
|
end
|
217
216
|
end
|
218
217
|
|
219
218
|
def find_or_create_worker
|
220
|
-
worker =
|
219
|
+
worker = workers.find { |item| item.running_task.nil? }
|
221
220
|
|
222
221
|
unless worker
|
223
222
|
@worker_id = (@worker_id || 0) + 1
|
224
223
|
worker = worker_class.new runner: self, name: "#{worker_class.to_s.gsub('::', '-')}-#{format '%02X', @worker_id}", use_threads: use_threads
|
225
224
|
worker.start
|
226
|
-
|
225
|
+
workers << worker
|
227
226
|
|
228
227
|
log_info "Worker created: #{worker}"
|
229
228
|
|
@@ -234,7 +233,7 @@ module Fasten
|
|
234
233
|
end
|
235
234
|
|
236
235
|
def dispatch_pending_tasks
|
237
|
-
while tasks.waiting? && tasks.running.count <
|
236
|
+
while tasks.waiting? && tasks.running.count < jobs
|
238
237
|
worker = find_or_create_worker
|
239
238
|
|
240
239
|
task = tasks.next
|
@@ -247,8 +246,8 @@ module Fasten
|
|
247
246
|
end
|
248
247
|
|
249
248
|
def remove_all_workers
|
250
|
-
|
251
|
-
|
249
|
+
workers.each(&:kill)
|
250
|
+
workers.clear
|
252
251
|
|
253
252
|
ui.force_clear
|
254
253
|
end
|
data/lib/fasten/support/stats.rb
CHANGED
@@ -112,8 +112,8 @@ module Fasten
|
|
112
112
|
filters: { 'run' => FLOAT_FORMATTER, 'avg' => FLOAT_FORMATTER, 'std' => FLOAT_FORMATTER, 'err' => FLOAT_FORMATTER },
|
113
113
|
description: false)
|
114
114
|
|
115
|
-
puts format('∑tasks: %<task>s runner: %<runner>s saved: %<saved>s
|
116
|
-
task: hformat(sub), runner: hformat(tot, sub), saved: hformat(sub - tot, sub),
|
115
|
+
puts format('∑tasks: %<task>s runner: %<runner>s saved: %<saved>s jobs: %<jobs>s',
|
116
|
+
task: hformat(sub), runner: hformat(tot, sub), saved: hformat(sub - tot, sub), jobs: jobs.to_s)
|
117
117
|
end
|
118
118
|
|
119
119
|
def stats_history(entry)
|
data/lib/fasten/task_manager.rb
CHANGED
@@ -2,7 +2,7 @@ module Fasten
|
|
2
2
|
class TaskManager < Array
|
3
3
|
attr_reader :done, :failed, :pending, :running, :targets
|
4
4
|
|
5
|
-
def initialize(targets:
|
5
|
+
def initialize(targets: [])
|
6
6
|
super()
|
7
7
|
|
8
8
|
@map = {}
|
@@ -53,6 +53,7 @@ module Fasten
|
|
53
53
|
|
54
54
|
reset
|
55
55
|
setup_dependencies
|
56
|
+
setup_targets
|
56
57
|
setup_scores
|
57
58
|
move_pending_to_waiting
|
58
59
|
end
|
@@ -87,8 +88,6 @@ module Fasten
|
|
87
88
|
@running = by_state.delete(:RUNNING) || []
|
88
89
|
@waiting = []
|
89
90
|
|
90
|
-
return unless @targets.nil? || @targets.empty?
|
91
|
-
|
92
91
|
@pending = by_state.values.flatten.each do |task|
|
93
92
|
task.depends = []
|
94
93
|
task.dependants = []
|
@@ -110,8 +109,37 @@ module Fasten
|
|
110
109
|
end
|
111
110
|
end
|
112
111
|
|
113
|
-
def
|
112
|
+
def setup_targets
|
113
|
+
return if @targets.empty?
|
114
|
+
|
115
|
+
@targets.each do |target|
|
116
|
+
task = target.is_a?(Task) ? target : @map[target]
|
117
|
+
raise "Target task #{target} not found" unless task
|
118
|
+
|
119
|
+
mark_needed(task)
|
120
|
+
end
|
121
|
+
|
122
|
+
@pending.reject { |task| task.state == :NEED }.each do |task|
|
123
|
+
@pending.delete task
|
124
|
+
delete task
|
125
|
+
end
|
126
|
+
|
114
127
|
@pending.each do |task|
|
128
|
+
task.state = nil
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def mark_needed(task)
|
133
|
+
return unless task.state == :IDLE
|
134
|
+
|
135
|
+
task.state = :NEED
|
136
|
+
task.depends.each do |depend_task|
|
137
|
+
mark_needed(depend_task)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def setup_scores
|
142
|
+
each do |task|
|
115
143
|
task.run_score = task.dependants.count
|
116
144
|
end
|
117
145
|
end
|
data/lib/fasten/ui/console.rb
CHANGED
@@ -5,8 +5,8 @@ module Fasten
|
|
5
5
|
class Console
|
6
6
|
extend Forwardable
|
7
7
|
|
8
|
-
def_delegators :runner, :
|
9
|
-
def_delegators :runner, :name, :
|
8
|
+
def_delegators :runner, :workers, :tasks
|
9
|
+
def_delegators :runner, :name, :jobs, :jobs=, :state, :state=, :hformat
|
10
10
|
|
11
11
|
attr_accessor :runner
|
12
12
|
|
@@ -20,7 +20,7 @@ module Fasten
|
|
20
20
|
puts <<~FIN
|
21
21
|
|
22
22
|
= == === ==== ===== ====== ======= ======== ========= ==========
|
23
|
-
Fasten your seatbelts! #{'💺' *
|
23
|
+
Fasten your seatbelts! #{'💺' * jobs} #{runner.use_threads ? 'threads' : 'processes'}
|
24
24
|
|
25
25
|
#{name}
|
26
26
|
FIN
|
data/lib/fasten/ui/curses.rb
CHANGED
@@ -9,8 +9,8 @@ module Fasten
|
|
9
9
|
include ::Curses
|
10
10
|
extend Forwardable
|
11
11
|
|
12
|
-
def_delegators :runner, :
|
13
|
-
def_delegators :runner, :name, :
|
12
|
+
def_delegators :runner, :workers, :tasks
|
13
|
+
def_delegators :runner, :name, :jobs, :jobs=, :state, :state=
|
14
14
|
|
15
15
|
attr_accessor :n_rows, :n_cols, :selected, :sel_index, :clear_needed, :message, :runner
|
16
16
|
|
@@ -28,7 +28,7 @@ module Fasten
|
|
28
28
|
ui_keyboard
|
29
29
|
clear if clear_needed
|
30
30
|
draw_title
|
31
|
-
|
31
|
+
ui_jobs
|
32
32
|
ui_tasks
|
33
33
|
|
34
34
|
refresh
|
@@ -96,15 +96,15 @@ module Fasten
|
|
96
96
|
self.message = nil
|
97
97
|
|
98
98
|
if key == Curses::Key::LEFT
|
99
|
-
if
|
99
|
+
if jobs <= 1
|
100
100
|
self.message = "Can't remove 1 worker left, press [P] to pause"
|
101
101
|
else
|
102
|
-
self.
|
103
|
-
self.message = "Decreasing
|
102
|
+
self.jobs -= 1
|
103
|
+
self.message = "Decreasing jobs to #{jobs}"
|
104
104
|
end
|
105
105
|
elsif key == Curses::Key::RIGHT
|
106
|
-
self.
|
107
|
-
self.message = "Increasing
|
106
|
+
self.jobs += 1
|
107
|
+
self.message = "Increasing jobs to #{jobs}"
|
108
108
|
elsif key == Curses::Key::DOWN
|
109
109
|
self.sel_index = sel_index ? [sel_index + 1, tasks.count - 1].min : 0
|
110
110
|
self.selected = tasks[sel_index]
|
@@ -124,18 +124,18 @@ module Fasten
|
|
124
124
|
force_clear
|
125
125
|
end
|
126
126
|
|
127
|
-
def
|
127
|
+
def ui_jobs_summary
|
128
128
|
running_count = tasks.running.count
|
129
129
|
waiting_count = tasks.waiting.count
|
130
|
-
workers_count =
|
130
|
+
workers_count = workers.count
|
131
131
|
|
132
|
-
"Procs running: #{running_count} idle: #{workers_count - running_count} waiting: #{waiting_count} #{runner.use_threads ? 'threads' : 'processes'}: #{
|
132
|
+
"Procs running: #{running_count} idle: #{workers_count - running_count} waiting: #{waiting_count} #{runner.use_threads ? 'threads' : 'processes'}: #{jobs}"
|
133
133
|
end
|
134
134
|
|
135
|
-
def
|
136
|
-
l = ui_text_aligned(1, :left,
|
135
|
+
def ui_jobs
|
136
|
+
l = ui_text_aligned(1, :left, ui_jobs_summary) + 1
|
137
137
|
|
138
|
-
|
138
|
+
workers.each_with_index do |worker, index|
|
139
139
|
setpos 1, l + index
|
140
140
|
attrs = worker.running? ? A_STANDOUT : color_pair(4) | A_DIM
|
141
141
|
attrset attrs
|
@@ -227,7 +227,7 @@ module Fasten
|
|
227
227
|
end
|
228
228
|
|
229
229
|
def ui_tasks
|
230
|
-
|
230
|
+
workers.each do |worker|
|
231
231
|
worker.spinner = (worker.spinner + 1) % SPINNER_LEN if worker.running?
|
232
232
|
end
|
233
233
|
|
data/lib/fasten/version.rb
CHANGED
data/lib/fasten.rb
CHANGED
@@ -63,7 +63,7 @@ module Fasten
|
|
63
63
|
@load_path = []
|
64
64
|
|
65
65
|
@opt_parser = OptionParser.new do |opts|
|
66
|
-
opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
66
|
+
opts.banner = "Usage: #{$PROGRAM_NAME} [options] [targets]"
|
67
67
|
opts.separator ''
|
68
68
|
opts.separator 'Examples:'
|
69
69
|
opts.separator ' fasten # load and run all task from fasten/*_fasten.rb'
|
@@ -72,26 +72,25 @@ module Fasten
|
|
72
72
|
opts.separator ''
|
73
73
|
opts.separator 'Options:'
|
74
74
|
|
75
|
-
opts.on '-n NAME', '--name
|
75
|
+
opts.on '-n NAME', '--name NAME', String, "Change name of this runner (default: #{default_name} from current directory)" do |name|
|
76
76
|
@options[:name] = name
|
77
77
|
end
|
78
|
-
opts.on '-f PATH', '--file
|
78
|
+
opts.on '-f PATH', '--file PATH', String, 'File or folder with ruby code' do |path|
|
79
79
|
@load_path << path
|
80
80
|
end
|
81
|
-
opts.on '-
|
82
|
-
@options[:
|
81
|
+
opts.on '-j JOBS', '--jobs JOBS', Numeric, "Maximum number of tasks to execute in parallel (default: #{default_jobs} on this machine)" do |jobs|
|
82
|
+
@options[:jobs] = jobs
|
83
83
|
end
|
84
84
|
opts.on '-s', '--[no-]summary', TrueClass, 'Display summary at the end of execution' do |boolean|
|
85
|
-
puts "SUMMAMRY: #{boolean}"
|
86
85
|
@options[:summary] = boolean
|
87
86
|
end
|
88
87
|
opts.on '--ui=UI', String, "Type of UI: curses, console. (default: #{default_ui_mode} on this machine)" do |ui_mode|
|
89
88
|
@options[:ui_mode] = ui_mode
|
90
89
|
end
|
91
|
-
opts.on '-t', '--threads', "Use threads
|
90
|
+
opts.on '-t', '--threads', "Use threads based jobs for parallel execution#{default_use_threads && ' (default on this machine)' || nil}" do
|
92
91
|
@options[:use_threads] = true
|
93
92
|
end
|
94
|
-
opts.on '-p', '--processes', "Use process
|
93
|
+
opts.on '-p', '--processes', "Use process based jobs for parallel execution#{!default_use_threads && ' (default on this machine)' || nil}" do
|
95
94
|
@options[:use_threads] = false
|
96
95
|
end
|
97
96
|
opts.on '-v', '--version', 'Display version info' do
|
@@ -112,7 +111,10 @@ module Fasten
|
|
112
111
|
def invoke
|
113
112
|
opt_parser.parse!
|
114
113
|
|
114
|
+
@options[:targets] = ARGV.to_a
|
115
|
+
|
115
116
|
runner @options
|
117
|
+
@load_path = Dir['fasten/*_fasten.rb'] if @load_path.empty?
|
116
118
|
load_fasten @load_path
|
117
119
|
|
118
120
|
show_help 1 if runner.tasks.empty?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fasten
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aldrin Martoq
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-11-
|
11
|
+
date: 2018-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|