fasten 0.6.0 → 0.7.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 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