fasten 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a62e1fda9d93b3a6740a65e81a53bcb4449910e770572df0b6c1a0d908e70cf
4
- data.tar.gz: 8bacfb093fe45101875b161fd9fb8385df565f60a1c5714b1866bb57a5e7eea6
3
+ metadata.gz: 2d815525a239cf976ae822a2a08d5c3be07ec006c07c7af991c68ce9bcf19b56
4
+ data.tar.gz: a9be85ef4553f17a9629315e55e6beb1dd28ed1db4c447964909826e5cd2650e
5
5
  SHA512:
6
- metadata.gz: 5b466b834558bc93de7d1c6cd01829e7930bc266bf8e0a3f34c34d379f5cc8425c0e60595650a1cc03869c1926eaa2993151406b05e51e4bdfd388a432d00394
7
- data.tar.gz: 1b47208c3c81702f184ac1878af2ac8fdc9d0390ad926feb7a8f7cb8552cb27a74b4171e372f12da5ec3b36305a229c650d139aa88b07314aaeee16a7bf4925b
6
+ metadata.gz: 3278db70005e8503299247c54eea40b4469de82e8729a4a839aa6d166070a96c4a71d1284d5bd3ab6702b19d080dddf74ef7a29ccc7a37a4308b452c2e854dbe
7
+ data.tar.gz: 43cc2129b54bfd13f4863cbc528e6d00ccf2a016976b389ff7a9dc6fbceff14f68e5b0d8ce555a28c6692ecb41f1ebfbfcc93dcaf0db4f9e1b21d3ad7de75464
data/.gitignore CHANGED
@@ -11,3 +11,4 @@
11
11
  .rspec_status
12
12
  .fasten
13
13
  *.testfile
14
+ .DS_Store
data/.rubocop.yml CHANGED
@@ -15,6 +15,6 @@ Metrics/AbcSize:
15
15
  Metrics/MethodLength:
16
16
  Max: 15
17
17
  Metrics/ParameterLists:
18
- Max: 6
18
+ Max: 8
19
19
  Layout/AlignHash:
20
20
  EnforcedHashRocketStyle: table
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fasten (0.6.0)
4
+ fasten (0.7.0)
5
5
  binding_of_caller
6
6
  hirb
7
7
  os
data/README.md CHANGED
@@ -1,24 +1,57 @@
1
- # Fasten your seatbelts!
2
-
3
- FIXME: Add intro here
4
-
5
- ## Roadmap
6
-
7
- - [x] define task to be run to reach a goal, with dependencies (similar to rake, make, and others)
8
- - [x] run task in parallel by default
9
- - [x] dynamicly change the number of worker processes
10
- - [x] allow to programatically define new tasks
11
- - [ ] allow to programatically redefine existing tasks
12
- - [x] keep each task log in a separate file, to easily debug errors
13
- - [x] early stop in case of failure
14
- - [x] for non-tty execution, report with a simple progress log in STDOUT
15
- - [ ] calculate ETA of script, based on previos executions of the same script
16
- - [ ] calculate ETA without previous information
17
- - [ ] for tty execution: start in background and provide a curses interface. When quit, the background process will keep running
18
- - [ ] provide a cli for running simple tasks
19
- - [x] provide a curses mode that displays number of workers, running tasks, remaining tasks, etc. Curses mode is the default.
20
- - [ ] display ETA in curses mode
21
- - [ ] curses mode can control (pause/stop/resume) other running executions
1
+ # Fasten your seatbelts! 💺💺💺💺
2
+
3
+ Run ruby code, shell code, or customized tasks in parallel using worker processes/threads. You can dynamically pause/continue/increase/decrease the number of tasks using our UI.
4
+
5
+ ![](demo/demo.gif)
6
+
7
+ **WARNING** this is a Work In Progress project:
8
+ - features are still being implemented
9
+ - API could and will change
10
+
11
+ It has been released so people can try it and make feature requests, comments and suggestions.
12
+
13
+ ## Feature Roadmap
14
+
15
+ ### Task definitions:
16
+ - [x] define tasks from an YAML file
17
+ - [x] tasks definition can include shell code or eval'ed ruby code
18
+ - [x] tasks can also be defined with blocks of code, like RSpec.describe
19
+ - [x] tasks can have dependencies that are needed to reach a goal, similar to rake, make, and others
20
+
21
+ ### Execution:
22
+ - [x] tasks are executed in parallel, using worker processes/threads
23
+ - [x] process are preferred on POSIX systems, otherwise threads are used
24
+ - [x] default number of workers is the # of real cpu cores on the system
25
+ - [x] in case of task Error or Exception, the whole run will be stopped
26
+ - [x] runner saves a log file in `$(pwd)/.fasten/log/runner/*name_of_runner*.log`
27
+ - [x] each task saves its STDERR/STDOUT to a log file in `$(pwd)/.fasten/log/task/*name_of_task*.log`
28
+ - [x] once finished, statistics are saved in `$HOME/.fasten/stats/*name_of_runner*.csv`, which include: status (DONE, FAIL), duration, average, stdev, error and count
29
+ - [x] display a summary table of the execution
30
+
31
+ ### UI/Curses
32
+ - [x] only available if the 'curses' gem is installed
33
+ - [x] Display number of workers running, idle, waiting, and number of workers (max)
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
+ - [x] Display the number of tasks done/pending
36
+ - [x] Display a progressbar
37
+ - [x] Display current local time
38
+ - [x] Pause whole runner with `P` key (waits current running tasks end)
39
+ - [x] Resume paused runner with `R` key
40
+ - [x] Press ⬅️ or ➡️ keys to dynamically increase/decrease number of workers
41
+ - [ ] Use ⬆️ and ⬇️ to select tasks
42
+ - [ ] Calculate ETA, assuming all tasks take same time
43
+ - [ ] Calculate ETA, based on saved tasks statistics
44
+ - [ ] Live tail -f of selected tasks
45
+
46
+ ### UI/Console
47
+ - [x] Console is the default if STDOUT/STDIN is not a tty
48
+ - [x] Display each task as is being finished
49
+ - [ ] Calculate ETA, assuming all tasks take same time
50
+ - [ ] Calculate ETA, based on saved tasks statistics
51
+
52
+
53
+ ### CLI
54
+ - [x] provides a CLI for running *_fasten.rb ruby code
22
55
 
23
56
 
24
57
  ## Installation
@@ -27,6 +60,7 @@ Add this line to your application's Gemfile:
27
60
 
28
61
  ```ruby
29
62
  gem 'fasten'
63
+ gem 'curses' # optional
30
64
  ```
31
65
 
32
66
  And then execute:
@@ -39,7 +73,7 @@ Or install it yourself as:
39
73
 
40
74
  ## Usage
41
75
 
42
- TODO: Write usage instructions here
76
+ TODO: Write usage instructions here. See spec for sample code.
43
77
 
44
78
  ## Development
45
79
 
data/fasten.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  # Specify which files should be added to the gem when it is released.
18
18
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
19
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
20
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|demo)/}) }
21
21
  end
22
22
  spec.bindir = 'exe'
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
data/lib/fasten/runner.rb CHANGED
@@ -20,20 +20,25 @@ module Fasten
20
20
  include Fasten::Support::UI
21
21
  include Fasten::Support::Yaml
22
22
 
23
- attr_accessor :name, :workers, :worker_class, :fasten_dir, :developer, :stats, :worker_list, :use_threads, :queue
23
+ attr_accessor :name, :workers, :worker_class, :fasten_dir, :developer, :stats, :summary, :ui_mode, :worker_list, :use_threads, :queue
24
24
 
25
- def initialize(name: nil, developer: STDIN.tty? && STDOUT.tty?, workers: Parallel.physical_processor_count, worker_class: Worker, fasten_dir: '.fasten', use_threads: !OS.posix?)
26
- reconfigure(name: name, developer: developer, workers: workers, worker_class: worker_class, fasten_dir: fasten_dir, use_threads: use_threads)
25
+ def initialize(name: nil,
26
+ developer: Fasten.default_developer, summary: nil, ui_mode: Fasten.default_ui_mode, workers: Fasten.default_workers,
27
+ worker_class: Worker, fasten_dir: '.fasten', use_threads: !OS.posix?)
28
+ reconfigure(name: name, developer: developer, summary: summary, ui_mode: ui_mode, workers: workers,
29
+ worker_class: worker_class, fasten_dir: fasten_dir, use_threads: use_threads)
27
30
  end
28
31
 
29
32
  def reconfigure(**options)
30
- self.stats = options[:name] && true if options[:name] || options.key?(:stats)
31
- self.name = options[:name] || "#{self.class} #{$PID}" if options[:name]
32
- self.workers = options[:workers] if options[:workers]
33
- self.worker_class = options[:worker_class] if options[:worker_class]
34
- self.fasten_dir = options[:fasten_dir] if options[:fasten_dir]
35
- self.developer = options[:developer] if options[:developer]
36
- self.use_threads = options[:use_threads] if options[:use_threads]
33
+ self.stats = options[:name] && true if options[:name] || options.key?(:stats)
34
+ self.name = options[:name] || "#{self.class} #{$PID}" if options.key?(:name)
35
+ self.workers = options[:workers] if options.key?(:workers)
36
+ self.worker_class = options[:worker_class] if options.key?(:worker_class)
37
+ self.fasten_dir = options[:fasten_dir] if options.key?(:fasten_dir)
38
+ self.developer = options[:developer] if options.key?(:developer)
39
+ self.use_threads = options[:use_threads] if options.key?(:use_threads)
40
+ self.summary = options[:summary] if options.key?(:summary)
41
+ self.ui_mode = options[:ui_mode] if options.key?(:ui_mode)
37
42
 
38
43
  initialize_dag
39
44
  initialize_stats
@@ -63,6 +68,8 @@ module Fasten
63
68
  log_fin self, running_counters
64
69
 
65
70
  stats_add_entry(state, self)
71
+
72
+ stats_summary if summary
66
73
  ensure
67
74
  save_stats
68
75
  end
@@ -76,7 +76,7 @@ module Fasten
76
76
 
77
77
  FLOAT_FORMATTER = ->(f) { format('%7.3f', f) }
78
78
 
79
- def stats_table_run
79
+ def stats_summary_data
80
80
  sub = stats_entries.select { |x| x['kind'] == 'task' }.map { |x| x['run'] }.sum
81
81
  tot = stats_entries.select { |x| x['kind'] == 'runner' }.map { |x| x['run'] }.sum
82
82
 
@@ -104,8 +104,8 @@ module Fasten
104
104
  str
105
105
  end
106
106
 
107
- def stats_table
108
- sub, tot = stats_table_run
107
+ def stats_summary
108
+ sub, tot = stats_summary_data
109
109
 
110
110
  Hirb::Console.render_output(stats_entries,
111
111
  fields: %w[state kind name run cnt avg std err worker], unicode: true, class: 'Hirb::Helpers::AutoTable',
@@ -5,7 +5,12 @@ module Fasten
5
5
  module UI
6
6
  def ui
7
7
  require 'fasten/ui/curses'
8
- @ui ||= STDIN.tty? && STDOUT.tty? ? Fasten::UI::Curses.new(runner: self) : Fasten::UI::Console.new(runner: self)
8
+
9
+ @ui ||= if ui_mode.to_s == 'curses' && STDIN.tty? && STDOUT.tty?
10
+ Fasten::UI::Curses.new(runner: self)
11
+ else
12
+ Fasten::UI::Console.new(runner: self)
13
+ end
9
14
  rescue StandardError, LoadError
10
15
  @ui = Fasten::UI::Console.new(runner: self)
11
16
  end
@@ -129,7 +129,7 @@ module Fasten
129
129
  waiting_count = task_waiting_list.count
130
130
  workers_count = worker_list.count
131
131
 
132
- "Procs run: #{running_count} idle: #{workers_count - running_count} #{runner.use_threads ? 'threads' : 'processes'}: #{workers} wait: #{waiting_count}"
132
+ "Procs running: #{running_count} idle: #{workers_count - running_count} waiting: #{waiting_count} #{runner.use_threads ? 'threads' : 'processes'}: #{workers}"
133
133
  end
134
134
 
135
135
  def ui_workers
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fasten
4
- VERSION = '0.6.0'
4
+ VERSION = '0.7.0'
5
5
  end
data/lib/fasten.rb CHANGED
@@ -35,6 +35,24 @@ module Fasten
35
35
  runner(**options).register(&block)
36
36
  end
37
37
 
38
+ def default_workers
39
+ Parallel.physical_processor_count
40
+ end
41
+
42
+ def default_developer
43
+ STDIN.tty? && STDOUT.tty?
44
+ end
45
+
46
+ def default_ui_mode
47
+ return @default_ui_mode if defined? @default_ui_mode
48
+
49
+ require 'fasten/ui/curses'
50
+
51
+ @default_ui_mode = :curses
52
+ rescue StandardError, LoadError
53
+ @default_ui_mode = :console
54
+ end
55
+
38
56
  def load_fasten(args)
39
57
  args.each do |path|
40
58
  if File.directory? path
@@ -61,25 +79,33 @@ module Fasten
61
79
  @opt_parser = OptionParser.new do |opts|
62
80
  opts.banner = "Usage: #{$PROGRAM_NAME} [options] FILE/DIRECTORY"
63
81
  opts.separator ''
64
- opts.separator 'Options'
82
+ opts.separator 'Example: fasten --name deploy --summary deploy_script.rb'
83
+ opts.separator ''
84
+ opts.separator 'Options:'
65
85
 
66
- opts.on '-n', '--name NAME', String, "Name of this job" do |name|
86
+ opts.on '-n NAME', '--name=NAME', String, 'Name of this runner, used to display and save stats' do |name|
67
87
  @options[:name] = name
68
88
  end
69
- opts.on '-w', '--workers WORKERS', Numeric, "Number of processes/threads to use (#{Parallel.physical_processor_count} on this machine)" do |workers|
89
+ opts.on '-w WORKERS', '--workers=WORKERS', Numeric, "Number of processes/threads to use (default: #{default_workers} on this machine)" do |workers|
70
90
  @options[:workers] = workers
71
91
  end
72
- opts.on '-t', '--threads' do
92
+ opts.on '-s', '--[no-]summary', TrueClass, 'Display summary at the end of execution' do |boolean|
93
+ @options[:summary] = boolean
94
+ end
95
+ opts.on '--ui=UI', String, "Type of UI: curses, console. (default: #{default_ui_mode} on this machine)" do |ui_mode|
96
+ @options[:ui_mode] = ui_mode
97
+ end
98
+ opts.on '-t', '--threads', 'Use threads workers for parallel execution' do
73
99
  @options[:use_threads] = true
74
100
  end
75
- opts.on '-p', '--processes' do
101
+ opts.on '-p', '--processes', 'Use process workers for parallel execution' do
76
102
  @options[:use_threads] = false
77
103
  end
78
- opts.on '-v', '--version' do
104
+ opts.on '-v', '--version', 'Display version info' do
79
105
  puts Fasten::VERSION
80
106
  exit 0
81
107
  end
82
- opts.on_tail '-h', '--help' do
108
+ opts.on_tail '-h', '--help', 'Shows this help' do
83
109
  puts opts
84
110
  exit 0
85
111
  end
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.6.0
4
+ version: 0.7.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-06 00:00:00.000000000 Z
11
+ date: 2018-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler