pretty_multitask 0.1.7 → 0.1.12

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: 855e2f7b1a3ddf9492a15002917bad4a6b72fd0bbec804e40adc12d9e9736730
4
- data.tar.gz: fb5d4f8224dd483d1dc6730fd4f9be2145aee5b3f0a560790524fa53a4a0882d
3
+ metadata.gz: f17834bf6e69de1ac60b9c3ef85057af450b3f2670c7436e95ce586bfa85abd5
4
+ data.tar.gz: f7489e37732d411b20f10b541ad8f0a8df6d989a3b6e9889f7bed027e649ffe4
5
5
  SHA512:
6
- metadata.gz: 855889a02269799b938d12af331850b8fa1f3e19a9f2ef912f46c04690a26e1160761ee85195538b82e26c383fb1c7a95cd565bf8bc5510d83db056a5e830b9e
7
- data.tar.gz: 4991776fa6b3ab42143f4e627e3c33a519a9fe099f9cf8c51b51931160214fd8f044352dff9cd62421e781ae43c90ade0a7330fa484f4f3d218137ebe16df098
6
+ metadata.gz: 79848302a5152c65f2262dfb30b045dd090270a8890caba3ef277df3924ebadd1a9b18babe82ac508dd61e1b5232b6ad9eac9e2f474f6f6b5fe0c95f9bb29f43
7
+ data.tar.gz: b3d559a22457161fb6b1d52dec37d3faeb5120eefb37df0e279582779e91b6ecd64132cb16e8f2f55d18c80511255d7fee633fd6ee6df211c7b9267794a39ca0
@@ -0,0 +1,35 @@
1
+ Style/SpecialGlobalVars:
2
+ Enabled: false
3
+
4
+ Style/ConditionalAssignment:
5
+ Enabled: false
6
+
7
+ Style/GuardClause:
8
+ Enabled: false
9
+
10
+ Layout/FirstArrayElementIndentation:
11
+ Enabled: false
12
+
13
+ Metrics/MethodLength:
14
+ Max: 35
15
+
16
+ Metrics/LineLength:
17
+ Max: 120
18
+ Exclude:
19
+ - test/**/*
20
+
21
+ Metrics/CyclomaticComplexity:
22
+ Max: 10
23
+
24
+ Metrics/AbcSize:
25
+ Max: 30
26
+
27
+ Security/MarshalLoad:
28
+ Exclude:
29
+ - lib/pretty_multitask/run_callable.rb
30
+
31
+ Layout/ElseAlignment:
32
+ Enabled: false
33
+ Metrics/BlockLength:
34
+ Exclude:
35
+ - '*.gemspec'
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  # Specify your gem's dependencies in pretty_multitask.gemspec
6
8
  gemspec
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
3
5
 
4
6
  Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
8
10
  end
9
11
 
10
- task :default => :test
12
+ task default: :test
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "pretty_multitask"
4
+ require 'bundler/setup'
5
+ require 'pretty_multitask'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +11,5 @@ require "pretty_multitask"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
@@ -1,4 +1,6 @@
1
- require "pretty_multitask/version"
1
+ # frozen_string_literal: true
2
+
3
+ require 'pretty_multitask/version'
2
4
  require 'tlopo/executor'
3
5
  require 'logger'
4
6
  require 'pty'
@@ -8,23 +10,27 @@ require 'socket'
8
10
  require 'timeout'
9
11
  require 'yaml'
10
12
  require 'fileutils'
13
+ require 'set'
11
14
 
15
+ # Main module
12
16
  module PrettyMultitask
17
+ require "#{__dir__}/pretty_multitask/trapper"
13
18
  require "#{__dir__}/pretty_multitask/color"
14
19
  require "#{__dir__}/pretty_multitask/runner"
15
20
  require "#{__dir__}/pretty_multitask/run_callable"
16
21
  end
17
22
 
18
23
  def pretty_multitask(hash)
19
- name, tasks = hash.keys.first, hash.values.first
24
+ name = hash.keys.first
25
+ tasks = hash.values.first
20
26
  task name do
21
27
  jobs = []
22
28
  tasks.each do |t|
23
- job = Proc.new do
29
+ job = proc do
24
30
  Rake::Task[t].invoke
25
31
  nil
26
32
  end
27
- jobs.push({ name: t, cmd: job})
33
+ jobs.push({ name: t, cmd: job })
28
34
  end
29
35
  PrettyMultitask::Runner.new(jobs).run
30
36
  end
@@ -1,14 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PrettyMultitask
4
+ # Class to provide some ANSI colors
2
5
  class Color
3
6
  def self.green(str)
4
7
  "\e[32;1m#{str}\e[0m"
5
8
  end
9
+
6
10
  def self.yellow(str)
7
11
  "\e[33;1m#{str}\e[0m"
8
12
  end
13
+
9
14
  def self.red(str)
10
15
  "\e[31;1m#{str}\e[0m"
11
16
  end
12
17
  end
13
18
  end
14
-
@@ -1,72 +1,93 @@
1
- module PrettyMultitask
1
+ # frozen_string_literal: true
2
2
 
3
+ module PrettyMultitask
4
+ # This class will run a callable, and wrap it's output adding nice format
3
5
  class RunCallable
4
6
  def initialize(opts = {})
5
7
  @opts = opts
6
8
  end
7
-
9
+
8
10
  def run(opts = @opts)
9
11
  cmd = opts[:cmd]
10
12
  name = opts[:name]
11
- out = opts[:out] || STDOUT
12
- err = opts[:err] || STDERR
13
+ out = STDOUT
13
14
  @padding = opts[:padding]
14
15
  master, slave = PTY.open
15
16
  err_read, err_write = IO.pipe
16
-
17
+
17
18
  r, w = UNIXSocket.pair(:STREAM, 0)
18
-
19
- pid = fork do
20
- STDERR.reopen err_write
21
- STDOUT.reopen master
22
- begin
23
- result = cmd.call
24
- obj = { result: result, error: nil }
25
- rescue StandardError => e
26
- new_error = e.class.new(e.message)
27
- new_error.set_backtrace e.backtrace
28
- Logger.new(STDERR).error new_error
29
- obj = { result: nil, error: new_error }
30
- File.open('/tmp/error','w+'){|f| f.puts obj.to_yaml}
31
- w.puts Marshal.dump(obj), 0
32
- end
33
- w.puts Marshal.dump(obj), 0
34
- w.close
35
- end
19
+
20
+ pid = run_on_fork(cmd, err_write, master, w)
21
+ Trapper.trap pid
36
22
 
37
23
  t_out = consume_and_print slave, out, name, false
38
24
  t_err = consume_and_print err_read, out, name, true
39
25
 
40
26
  chars = []
27
+ t = consume_and_store r, chars
28
+
29
+ Process.wait pid
30
+
31
+ wait_until_streams_are_ready [slave, err_read]
32
+
33
+ join_threads [t_out, t_err, t]
34
+
35
+ close_streams [master, slave, err_read, err_write]
36
+
37
+ result = Marshal.load(chars.join)
38
+ raise result[:error] if result[:error]
39
+
40
+ result[:result]
41
+ end
42
+
43
+ def consume_and_store(reader, store)
41
44
  t = Thread.new do
42
- sleep 0.1 until r.ready?
43
- (1..3).each do
44
- chars << r.getc while r.ready?
45
+ sleep 0.1 until reader.ready?
46
+ 3.times do
47
+ store << reader.getc while reader.ready?
45
48
  end
46
49
  end
50
+ t
51
+ end
47
52
 
48
- Process.wait pid
49
- Timeout.timeout(1) { t.join }
50
-
51
- %i[slave err_read].each do |e|
52
- loop { break unless binding.local_variable_get(e).ready? }
53
+ def wait_until_streams_are_ready(streams)
54
+ streams.each do |s|
55
+ loop { break unless s.ready? }
53
56
  end
54
-
55
- begin
56
- Timeout.timeout(0.1) do
57
- %i[t_out t_err].each { |e| binding.local_variable_get(e).join }
57
+ end
58
+
59
+ def join_threads(threads)
60
+ threads.each do |t|
61
+ begin
62
+ Timeout.timeout(0.1) { t.join }
63
+ rescue Timeout::Error
64
+ nil
58
65
  end
59
- rescue Timeout::Error
60
- nil
61
66
  end
62
-
63
- %i[master slave err_read err_write].each { |e| binding.local_variable_get(e).close }
64
-
65
-
66
- result = Marshal.load(chars.join)
67
- raise result[:error] if result[:error]
68
-
69
- result[:result]
67
+ end
68
+
69
+ def close_streams(streams)
70
+ streams.each(&:close)
71
+ end
72
+
73
+ def run_on_fork(cmd, err_w, out_w, socket_w)
74
+ pid = fork do
75
+ STDERR.reopen err_w
76
+ STDOUT.reopen out_w
77
+ begin
78
+ result = cmd.call
79
+ obj = { result: result, error: nil }
80
+ rescue StandardError => e
81
+ new_error = e.class.new(e.message)
82
+ new_error.set_backtrace e.backtrace
83
+ Logger.new(STDERR).error new_error
84
+ obj = { result: nil, error: new_error }
85
+ socket_w.puts Marshal.dump(obj), 0
86
+ end
87
+ socket_w.puts Marshal.dump(obj), 0
88
+ socket_w.close
89
+ end
90
+ pid
70
91
  end
71
92
 
72
93
  def consume_and_print(reader, writer, name, error = false)
@@ -77,10 +98,12 @@ module PrettyMultitask
77
98
  colored = Color.green format(fmt, name) unless error
78
99
  reader.each_line do |line|
79
100
  writer.write "#{colored} #{line}"
80
- File.open(@opts[:out_file],'a+') do |f|
101
+ next unless @opts[:out_file]
102
+
103
+ File.open(@opts[:out_file], 'a+') do |f|
81
104
  f.puts "#{colored} #{line}" if error
82
105
  f.puts "#{colored} #{line}" unless error
83
- end if @opts[:out_file]
106
+ end
84
107
  end
85
108
  rescue Errno::EIO
86
109
  nil
@@ -1,47 +1,55 @@
1
- module PrettyMultitask
1
+ # frozen_string_literal: true
2
2
 
3
- LOGGER ||= Logger.new STDOUT
3
+ module PrettyMultitask
4
+ # This class will run multiple callables in parallel using RunCallable which add a nice format
4
5
  class Runner
6
+ LOGGER ||= Logger.new STDOUT
5
7
  def initialize(jobs)
6
8
  @jobs = jobs
7
9
  @jobs.each do |j|
8
- [:name, :cmd ].each {|o| raise "#{o} must be specified for job #{j}" unless j[o] }
10
+ %i[name cmd].each { |o| raise "#{o} must be specified for job #{j}" unless j[o] }
9
11
  j[:out_file] = "/tmp/#{j[:name]}-#{Time.now.strftime('%s.%N')}"
10
12
  FileUtils.touch j[:out_file]
11
13
  end
12
14
  end
13
-
15
+
14
16
  def run
15
17
  exec = Tlopo::Executor.new 10
16
-
17
- longest= 0
18
- @jobs.each {|job| longest = job[:name].length if job[:name].length > longest }
19
- @jobs.each {|job| job[:padding] = longest }
20
-
18
+
19
+ @jobs.each { |job| job[:padding] = longest_jobname }
21
20
  @jobs.each do |j|
22
21
  task = proc { j[:exit_status] = RunCallable.new(j).run }
23
22
  exec.schedule task
24
23
  end
25
24
  errors = exec.run.errors
26
-
25
+
26
+ print_out
27
+
28
+ unless errors.empty?
29
+ errors.each { |e| LOGGER.error e }
30
+ raise 'Found errors'
31
+ end
32
+ end
33
+
34
+ def print_out
27
35
  @jobs.each do |j|
28
36
  label = "[ #{j[:name]} ]"
29
37
  width = IO.console.winsize[-1]
30
- left = '='*((width - label.length)/2)
38
+ width = 80 unless width > 0
39
+ left = '=' * ((width - label.length) / 2)
31
40
  right = j[:name].length.even? ? left : left + '='
32
41
  puts "\n"
33
- puts Color.yellow left + label + right
42
+ puts Color.yellow left + label + right
34
43
  puts File.read j[:out_file]
35
- puts Color.yellow "="*width
44
+ puts Color.yellow '=' * width
36
45
  File.delete j[:out_file]
37
46
  end
38
-
39
- unless errors.empty?
40
- errors.each {|e| LOGGER.error e}
41
- raise 'Found errors'
42
- end
43
47
  end
44
-
45
- end
46
48
 
49
+ def longest_jobname
50
+ longest = 0
51
+ @jobs.each { |job| longest = job[:name].length if job[:name].length > longest }
52
+ longest
53
+ end
54
+ end
47
55
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrettyMultitask
4
+ module Trapper
5
+ module_function
6
+
7
+ PIDS ||= Set.new
8
+ def trap(pid)
9
+ PIDS << pid
10
+ %w[SIGINT SIGTERM SIGHUP].each do |sig|
11
+ Signal.trap(sig) do
12
+ begin
13
+ PIDS.each { |pid| Process.kill sig, pid }
14
+ rescue Errno::ESRCH
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PrettyMultitask
2
- VERSION = "0.1.7"
4
+ VERSION = '0.1.12'
3
5
  end
@@ -1,38 +1,40 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "pretty_multitask/version"
5
+ require 'pretty_multitask/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "pretty_multitask"
8
+ spec.name = 'pretty_multitask'
8
9
  spec.version = PrettyMultitask::VERSION
9
- spec.authors = ["Tiago Lopo Da Silva"]
10
- spec.email = ["tiagolopo@yahoo.com.br"]
10
+ spec.authors = ['Tiago Lopo Da Silva']
11
+ spec.email = ['tiagolopo@yahoo.com.br']
11
12
 
12
- spec.summary = %q{Multitask with pretty output for Rake}
13
- spec.description = %q{Multitask with pretty output for Rake}
14
- spec.homepage = "https://github.com/tlopo-ruby/tlopo-executor"
15
- spec.license = "MIT"
13
+ spec.summary = 'Multitask with pretty output for Rake'
14
+ spec.description = 'Multitask with pretty output for Rake'
15
+ spec.homepage = 'https://github.com/tlopo-ruby/tlopo-executor'
16
+ spec.license = 'MIT'
16
17
 
17
18
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
19
  # to allow pushing to a single host or delete this section to allow pushing to any host.
19
20
  if spec.respond_to?(:metadata)
20
- #spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
22
  else
22
- raise "RubyGems 2.0 or newer is required to protect against " \
23
- "public gem pushes."
23
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
24
+ 'public gem pushes.'
24
25
  end
25
26
 
26
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
28
  f.match(%r{^(test|spec|features)/})
28
29
  end
29
- spec.bindir = "exe"
30
+ spec.bindir = 'exe'
30
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
- spec.require_paths = ["lib"]
32
+ spec.require_paths = ['lib']
32
33
 
33
- spec.add_dependency "tlopo-executor", "~> 0.1.0"
34
+ spec.add_dependency 'tlopo-executor', '~> 0.1.0'
35
+ spec.add_dependency 'io-console', '~> 0.4'
34
36
 
35
- spec.add_development_dependency "bundler", "~> 1.15"
36
- spec.add_development_dependency "rake", "~> 10.0"
37
- spec.add_development_dependency "minitest", "~> 5.0"
37
+ spec.add_development_dependency 'bundler', '~> 2.1'
38
+ spec.add_development_dependency 'minitest', '~> 5.0'
39
+ spec.add_development_dependency 'rake', '~> 12.3.3'
38
40
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pretty_multitask
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Lopo Da Silva
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-09 00:00:00.000000000 Z
11
+ date: 2021-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tlopo-executor
@@ -25,33 +25,33 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.1.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: io-console
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.15'
34
- type: :development
33
+ version: '0.4'
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.15'
40
+ version: '0.4'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
42
+ name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '2.1'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: '2.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 12.3.3
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 12.3.3
69
83
  description: Multitask with pretty output for Rake
70
84
  email:
71
85
  - tiagolopo@yahoo.com.br
@@ -74,6 +88,7 @@ extensions: []
74
88
  extra_rdoc_files: []
75
89
  files:
76
90
  - ".gitignore"
91
+ - ".rubocop.yml"
77
92
  - ".travis.yml"
78
93
  - CODE_OF_CONDUCT.md
79
94
  - Gemfile
@@ -86,13 +101,14 @@ files:
86
101
  - lib/pretty_multitask/color.rb
87
102
  - lib/pretty_multitask/run_callable.rb
88
103
  - lib/pretty_multitask/runner.rb
104
+ - lib/pretty_multitask/trapper.rb
89
105
  - lib/pretty_multitask/version.rb
90
106
  - pretty_multitask.gemspec
91
107
  homepage: https://github.com/tlopo-ruby/tlopo-executor
92
108
  licenses:
93
109
  - MIT
94
110
  metadata: {}
95
- post_install_message:
111
+ post_install_message:
96
112
  rdoc_options: []
97
113
  require_paths:
98
114
  - lib
@@ -107,8 +123,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
123
  - !ruby/object:Gem::Version
108
124
  version: '0'
109
125
  requirements: []
110
- rubygems_version: 3.0.3
111
- signing_key:
126
+ rubygems_version: 3.1.2
127
+ signing_key:
112
128
  specification_version: 4
113
129
  summary: Multitask with pretty output for Rake
114
130
  test_files: []