command_exec 0.1.3 → 0.2.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.
- data/Gemfile +6 -2
- data/Gemfile.lock +42 -18
- data/README.md +707 -72
- data/RELEASE_NOTES.md +62 -0
- data/Rakefile +40 -9
- data/TODO.md +8 -2
- data/command_exec.gemspec +3 -2
- data/gemfiles/Gemfile.default +28 -0
- data/gemfiles/Gemfile.travis +16 -0
- data/gemfiles/Gemfile.travis.lock +48 -0
- data/lib/command_exec.rb +22 -2
- data/lib/command_exec/command.rb +307 -157
- data/lib/command_exec/exceptions.rb +16 -6
- data/lib/command_exec/field_helper.rb +263 -0
- data/lib/command_exec/formatter/array.rb +158 -0
- data/lib/command_exec/formatter/hash.rb +78 -0
- data/lib/command_exec/formatter/json.rb +22 -0
- data/lib/command_exec/formatter/string.rb +22 -0
- data/lib/command_exec/formatter/xml.rb +22 -0
- data/lib/command_exec/formatter/yaml.rb +22 -0
- data/lib/command_exec/logger.rb +9 -0
- data/lib/command_exec/process.rb +294 -0
- data/lib/command_exec/spec_helper_module.rb +52 -0
- data/lib/command_exec/version.rb +1 -1
- data/script/console +8 -0
- data/spec/command/command_spec.rb +413 -117
- data/spec/command/test_data/echo_test +3 -0
- data/spec/command/test_data/exit_status_test +2 -0
- data/spec/command/test_data/log_file_test +3 -0
- data/spec/command/test_data/logger_test +2 -0
- data/spec/command/test_data/not_raise_error_test +4 -0
- data/spec/command/test_data/not_throw_error_test +4 -0
- data/spec/command/test_data/output_test +6 -0
- data/spec/command/test_data/raise_error_test +6 -0
- data/spec/command/test_data/runner_open3_test +4 -0
- data/spec/command/test_data/runner_system_test +4 -0
- data/spec/command/test_data/stderr_test +4 -0
- data/spec/command/test_data/stdout_multiple_lines_test +4 -0
- data/spec/command/test_data/stdout_test +4 -0
- data/spec/command/test_data/throw_error_test +6 -0
- data/spec/command/test_data/true_test +2 -0
- data/spec/formatter/array_spec.rb +215 -0
- data/spec/formatter/hash_spec.rb +117 -0
- data/spec/formatter/json_spec.rb +21 -0
- data/spec/formatter/xml_spec.rb +33 -0
- data/spec/formatter/yaml_spec.rb +21 -0
- data/spec/process/process_spec.rb +329 -0
- data/spec/spec_helper.rb +15 -4
- metadata +79 -5
data/RELEASE_NOTES.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# 0.2.0
|
2
|
+
|
3
|
+
This release is a major rewrite of the library. Please keep in mind it is still
|
4
|
+
under very active development and till 1.0.0 the API can change at any time.
|
5
|
+
Though I'm quite satisfied with the api and will try to add things only.
|
6
|
+
|
7
|
+
* *TREATMENT OF COMMAND NAMES*:
|
8
|
+
|
9
|
+
Now only commands given as symbols will be searched in search path. Which can
|
10
|
+
be set as well now. If a string is given, it will be searched in the current
|
11
|
+
working directory. Furthermore the resolver was refactored. It is now a ruby
|
12
|
+
only solution.
|
13
|
+
|
14
|
+
* *TEST SUITE*:
|
15
|
+
|
16
|
+
Improved test suite. Added more tests and add `simplecov` for coverage check.
|
17
|
+
|
18
|
+
* *WORKING DIRECTORY*:
|
19
|
+
|
20
|
+
No side effects any more when using a working directory. This might have had an
|
21
|
+
effect on your library if you start your script from directory 'A', but want the
|
22
|
+
command to be run in directory 'B'. After running the command with `CommandExec`
|
23
|
+
the current working directory of your script was changed as well. This is fixed
|
24
|
+
now.
|
25
|
+
|
26
|
+
* *LOGGING*:
|
27
|
+
|
28
|
+
Now you have the possibility to change the log level of `CommandExec`. If you
|
29
|
+
choose one of `:silent`, `:unknown`, `:fatal`, `:error`, `:warn`, `:info` and
|
30
|
+
`:debug`. All possibilities map to their `Logger`-counterpart, so please see
|
31
|
+
the ruby logger documentation on the net (google for ruby logger). The only
|
32
|
+
exception is `:silent`. Choosing this option `CommandExec` will not output
|
33
|
+
anything.
|
34
|
+
|
35
|
+
* *VERSIONING*:
|
36
|
+
|
37
|
+
This gem now uses semvar versioning scheme. See www.semvar.org for more information.
|
38
|
+
|
39
|
+
* *RESULT OF COMMAND EXECUTION*:
|
40
|
+
|
41
|
+
Now it's possible that you get the result of the command execution in different formats:
|
42
|
+
* Array
|
43
|
+
* Hash
|
44
|
+
* JSON-String
|
45
|
+
* Simple String
|
46
|
+
* XML-String
|
47
|
+
* YAML-String
|
48
|
+
|
49
|
+
* *ERROR DETECTION*:
|
50
|
+
|
51
|
+
As part of the new interface there are methods put in place to detect errors
|
52
|
+
during command execution: Return code, STDERR, STDOUT and log file.
|
53
|
+
|
54
|
+
* *REACTION ON ERRORS*:
|
55
|
+
|
56
|
+
If an error happend, you can raise an exception, throw an error or ask
|
57
|
+
`CommandExec`to do nothing at all.
|
58
|
+
|
59
|
+
* *COMMAND RUNNER*
|
60
|
+
|
61
|
+
The new version switches from `POpen4` to `Open3` as default runner.
|
62
|
+
Furthermore `system`-call is also supported.
|
data/Rakefile
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
require '
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
2
|
+
|
3
|
+
unless ENV['TRAVIS_CI'] == 'true'
|
4
|
+
namespace :gem do
|
5
|
+
require 'bundler/gem_tasks'
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'yard'
|
9
|
+
require 'rubygems/package_task'
|
10
|
+
require 'active_support/core_ext/string/strip'
|
10
11
|
end
|
11
12
|
|
13
|
+
YARD::Rake::YardocTask.new do; end
|
14
|
+
|
15
|
+
desc 'start tmux'
|
12
16
|
task :terminal do
|
13
17
|
sh "script/terminal"
|
14
18
|
end
|
@@ -19,9 +23,10 @@ task :t => :terminal
|
|
19
23
|
namespace :version do
|
20
24
|
version_file = Dir.glob('lib/**/version.rb').first
|
21
25
|
|
26
|
+
desc 'bump version of library to new version'
|
22
27
|
task :bump do
|
23
28
|
|
24
|
-
new_version = ENV['VERSION']
|
29
|
+
new_version = ENV['VERSION'] || ENV['version']
|
25
30
|
|
26
31
|
raw_module_name = File.open(version_file, "r").readlines.grep(/module/).first
|
27
32
|
module_name = raw_module_name.chomp.match(/module\s+(\S+)/) {$1}
|
@@ -40,6 +45,7 @@ end}
|
|
40
45
|
sh "git tag data_uri-#{new_version}"
|
41
46
|
end
|
42
47
|
|
48
|
+
desc 'show version of library'
|
43
49
|
task :show do
|
44
50
|
raw_version = File.open(version_file, "r").readlines.grep(/VERSION/).first
|
45
51
|
|
@@ -52,8 +58,33 @@ end}
|
|
52
58
|
|
53
59
|
end
|
54
60
|
|
61
|
+
desc 'Restore version file from git repository'
|
55
62
|
task :restore do
|
56
63
|
sh "git checkout #{version_file}"
|
57
64
|
end
|
58
65
|
|
59
66
|
end
|
67
|
+
|
68
|
+
namespace :travis do
|
69
|
+
desc 'Runs travis-lint to check .travis.yml'
|
70
|
+
task :check do
|
71
|
+
sh 'travis-lint'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
namespace :test do
|
76
|
+
desc 'Run specs'
|
77
|
+
task :specs do
|
78
|
+
sh 'bundle exec rspec spec'
|
79
|
+
end
|
80
|
+
|
81
|
+
desc 'Run tests in "travis mode"'
|
82
|
+
task :travis_specs do
|
83
|
+
ENV['TRAVIS_CI'] = 'true'
|
84
|
+
sh 'rspec spec'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
task :console do
|
89
|
+
sh 'script/console'
|
90
|
+
end
|
data/TODO.md
CHANGED
@@ -1,2 +1,8 @@
|
|
1
|
-
* Add
|
2
|
-
*
|
1
|
+
* Add tests for search command
|
2
|
+
* Support shell environment for command
|
3
|
+
* Support Time of run in output
|
4
|
+
* Support run time in output
|
5
|
+
* Support stdin
|
6
|
+
* Limit output to n lines
|
7
|
+
* YAML should be the same output like the others
|
8
|
+
* Support for Proc as an reaction on an error
|
data/command_exec.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.require_paths = ['lib']
|
18
18
|
|
19
19
|
# specify any dependencies here; for example:
|
20
|
-
s.add_runtime_dependency '
|
21
|
-
s.add_runtime_dependency '
|
20
|
+
s.add_runtime_dependency 'smart_colored'
|
21
|
+
s.add_runtime_dependency 'activesupport'
|
22
|
+
s.add_runtime_dependency 'xml-simple'
|
22
23
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in workplace-letter_generator.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem 'rake'
|
8
|
+
gem 'rspec'
|
9
|
+
gem 'simplecov'
|
10
|
+
gem 'aruba'
|
11
|
+
gem 'fuubar'
|
12
|
+
end
|
13
|
+
|
14
|
+
group :documentation do
|
15
|
+
gem 'yard'
|
16
|
+
gem 'redcarpet'
|
17
|
+
gem 'github-markup'
|
18
|
+
end
|
19
|
+
|
20
|
+
group :development do
|
21
|
+
gem 'tmrb'
|
22
|
+
gem 'debugger'
|
23
|
+
gem 'pry'
|
24
|
+
gem 'pry-doc'
|
25
|
+
gem 'awesome_print'
|
26
|
+
gem 'travis-lint'
|
27
|
+
gem 'activesupport'
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in workplace-letter_generator.gemspec
|
4
|
+
#gemspec path: '../command_exec.gemspec'
|
5
|
+
gemspec path: '../'
|
6
|
+
|
7
|
+
group :test do
|
8
|
+
gem 'rake'
|
9
|
+
gem 'rspec'
|
10
|
+
end
|
11
|
+
|
12
|
+
group :documentation do
|
13
|
+
gem 'yard'
|
14
|
+
gem 'redcarpet'
|
15
|
+
gem 'github-markup'
|
16
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
PATH
|
2
|
+
remote: /home/d/work/projects/ruby-command_exec
|
3
|
+
specs:
|
4
|
+
command_exec (0.1.3)
|
5
|
+
POpen4
|
6
|
+
activesupport
|
7
|
+
colored (>= 1.2)
|
8
|
+
xml-simple
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: http://rubygems.org/
|
12
|
+
specs:
|
13
|
+
POpen4 (0.1.4)
|
14
|
+
Platform (>= 0.4.0)
|
15
|
+
open4
|
16
|
+
Platform (0.4.0)
|
17
|
+
activesupport (3.2.8)
|
18
|
+
i18n (~> 0.6)
|
19
|
+
multi_json (~> 1.0)
|
20
|
+
colored (1.2)
|
21
|
+
diff-lcs (1.1.3)
|
22
|
+
github-markup (0.7.4)
|
23
|
+
i18n (0.6.1)
|
24
|
+
multi_json (1.3.6)
|
25
|
+
open4 (1.3.0)
|
26
|
+
rake (0.9.2.2)
|
27
|
+
redcarpet (2.1.1)
|
28
|
+
rspec (2.11.0)
|
29
|
+
rspec-core (~> 2.11.0)
|
30
|
+
rspec-expectations (~> 2.11.0)
|
31
|
+
rspec-mocks (~> 2.11.0)
|
32
|
+
rspec-core (2.11.1)
|
33
|
+
rspec-expectations (2.11.3)
|
34
|
+
diff-lcs (~> 1.1.3)
|
35
|
+
rspec-mocks (2.11.2)
|
36
|
+
xml-simple (1.1.1)
|
37
|
+
yard (0.8.2.1)
|
38
|
+
|
39
|
+
PLATFORMS
|
40
|
+
ruby
|
41
|
+
|
42
|
+
DEPENDENCIES
|
43
|
+
command_exec!
|
44
|
+
github-markup
|
45
|
+
rake
|
46
|
+
redcarpet
|
47
|
+
rspec
|
48
|
+
yard
|
data/lib/command_exec.rb
CHANGED
@@ -1,11 +1,31 @@
|
|
1
1
|
#encoding: utf-8
|
2
2
|
|
3
|
-
require '
|
4
|
-
require '
|
3
|
+
require 'smart_colored/extend'
|
4
|
+
require 'json'
|
5
|
+
require 'psych'
|
6
|
+
require 'xmlsimple'
|
7
|
+
require 'open3'
|
8
|
+
|
5
9
|
require 'logger'
|
10
|
+
require 'command_exec/logger'
|
11
|
+
|
12
|
+
require 'active_support/core_ext/object/blank'
|
13
|
+
require 'active_support/core_ext/string/filters'
|
14
|
+
require 'active_support/core_ext/hash/deep_merge'
|
15
|
+
require 'active_support/core_ext/hash/conversions'
|
16
|
+
|
17
|
+
require 'command_exec/field_helper'
|
18
|
+
|
19
|
+
require 'command_exec/formatter/array'
|
20
|
+
require 'command_exec/formatter/hash'
|
21
|
+
require 'command_exec/formatter/json'
|
22
|
+
require 'command_exec/formatter/yaml'
|
23
|
+
require 'command_exec/formatter/xml'
|
24
|
+
require 'command_exec/formatter/string'
|
6
25
|
|
7
26
|
require 'command_exec/version'
|
8
27
|
require 'command_exec/exceptions'
|
9
28
|
require 'command_exec/command'
|
29
|
+
require 'command_exec/process'
|
10
30
|
|
11
31
|
module CommandExec; end
|
data/lib/command_exec/command.rb
CHANGED
@@ -5,49 +5,190 @@ module CommandExec
|
|
5
5
|
# Run commands
|
6
6
|
class Command
|
7
7
|
|
8
|
-
|
8
|
+
# @!attribute [rw] log_file
|
9
|
+
# Set/Get log file for command
|
10
|
+
#
|
11
|
+
# @!attribute [rw] options
|
12
|
+
# Set/Get options for the command
|
13
|
+
#
|
14
|
+
# @!attribute [rw] parameter
|
15
|
+
# Set/Get parameter for the command
|
16
|
+
attr_accessor :log_file, :options , :parameter
|
17
|
+
|
18
|
+
# @!attribute [r] result
|
19
|
+
# Return the result of command execution
|
20
|
+
#
|
21
|
+
# @!attribute [r] path
|
22
|
+
# Return path to the executable of the command
|
23
|
+
#
|
24
|
+
# @!attribute [r] working_directory
|
25
|
+
# Return the working directory of the command
|
9
26
|
attr_reader :result, :path, :working_directory
|
10
27
|
|
11
28
|
# Create a new command to execute
|
12
29
|
#
|
13
|
-
# @param [Symbol] name
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# @
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# @option opts [String] :
|
30
|
+
# @param [Symbol] name
|
31
|
+
# name of command
|
32
|
+
#
|
33
|
+
# @param [optional,Hash] opts
|
34
|
+
# options for the command
|
35
|
+
#
|
36
|
+
# @option opts [String] :options ('')
|
37
|
+
# options for the command
|
38
|
+
#
|
39
|
+
# @option opts [String] :working_directory (current working directory)
|
40
|
+
# working_directory for the command
|
41
|
+
#
|
42
|
+
# @option opts [String] :log_file ('')
|
43
|
+
# log file of the command
|
44
|
+
#
|
45
|
+
# @option opts [String,Array] :search_paths ($PATH)
|
46
|
+
# where to search for the command (please mind the 's' at the end.
|
47
|
+
#
|
48
|
+
# @option opts [String,Array] :error_detection_on (:return_code)
|
49
|
+
# what information should be considered for error detection,
|
50
|
+
# available options are :return_code, :stderr, :stdout, :log_file.
|
51
|
+
# You can use one or more of them.
|
52
|
+
#
|
53
|
+
# @option opts [Hash] :error_indicators
|
54
|
+
# what keywords etc. should be considered as errors.
|
55
|
+
#
|
56
|
+
# You can define allowed or forbidden keywords or exit codes.
|
57
|
+
# To search for errors in a log file you need to provide one.
|
58
|
+
#
|
59
|
+
# For each option you can provide a single word or an Array of words.
|
60
|
+
#
|
61
|
+
# ```
|
62
|
+
# :allowed_return_code => [0],
|
63
|
+
# :forbidden_return_code => [],
|
64
|
+
# :allowed_words_in_stderr => [],
|
65
|
+
# :forbidden_words_in_stderr => [],
|
66
|
+
# :allowed_words_in_stdout => [],
|
67
|
+
# :forbidden_words_in_stdout => [],
|
68
|
+
# :allowed_words_in_log_file => [],
|
69
|
+
# :forbidden_words_in_log_file => [],
|
70
|
+
# ```
|
71
|
+
#
|
72
|
+
# @option opts [Symbol] :on_error_do
|
73
|
+
# Oh, an error happend, what to do next? Raise an error (:raise_error),
|
74
|
+
# Throw an error (:throw_error) or do nothing at all (:nothing, default).
|
75
|
+
#
|
76
|
+
# @option opts [Symbol] :run_via
|
77
|
+
# Which runner should be used to execute the command: :open3 (default)
|
78
|
+
# or :system.
|
79
|
+
#
|
80
|
+
# @option opts [Logger] :lib_logger
|
81
|
+
# The logger which is used to output information generated by the
|
82
|
+
# library. The logger which is provided needs to be compatible with api
|
83
|
+
# of the Ruby `Logger`-class.
|
84
|
+
#
|
85
|
+
# @option opts [Symbol] :lib_log_level
|
86
|
+
# What information should handled by the logger:
|
87
|
+
# :debug, :info, :warn, :error, :fatal, :unknown. Additionally the
|
88
|
+
# :silent-option is understood: do not output anything (@see README for
|
89
|
+
# further information).
|
20
90
|
def initialize(name,opts={})
|
21
91
|
|
22
92
|
@name = name
|
23
93
|
@opts = {
|
24
|
-
:logger => Logger.new($stderr),
|
25
94
|
:options => '',
|
26
95
|
:parameter => '',
|
27
|
-
:error_keywords => [],
|
28
96
|
:working_directory => Dir.pwd,
|
29
|
-
:
|
30
|
-
:
|
31
|
-
|
97
|
+
:log_file => '',
|
98
|
+
:search_paths => ENV['PATH'].split(':'),
|
99
|
+
:error_detection_on => [:return_code],
|
100
|
+
:error_indicators => {
|
101
|
+
:allowed_return_code => [0],
|
102
|
+
:forbidden_return_code => [],
|
103
|
+
#
|
104
|
+
:allowed_words_in_stderr => [],
|
105
|
+
:forbidden_words_in_stderr => [],
|
106
|
+
#
|
107
|
+
:allowed_words_in_stdout => [],
|
108
|
+
:forbidden_words_in_stdout => [],
|
109
|
+
#
|
110
|
+
:allowed_words_in_log_file => [],
|
111
|
+
:forbidden_words_in_log_file => [],
|
112
|
+
},
|
113
|
+
:on_error_do => :nothing,
|
114
|
+
:run_via => :open3,
|
115
|
+
:lib_logger => Logger.new($stderr),
|
116
|
+
:lib_log_level => :info,
|
117
|
+
}.deep_merge opts
|
118
|
+
|
119
|
+
@logger = @opts[:lib_logger]
|
120
|
+
configure_logging
|
121
|
+
|
122
|
+
@logger.debug @opts
|
32
123
|
|
33
|
-
@logger = @opts[:logger]
|
34
124
|
@options = @opts[:options]
|
125
|
+
@path = resolve_path @name, @opts[:search_paths]
|
35
126
|
@parameter = @opts[:parameter]
|
36
|
-
@
|
37
|
-
@error_keywords = @opts[:error_keywords]
|
38
|
-
@logfile = @opts[:logfile]
|
127
|
+
@log_file = @opts[:log_file]
|
39
128
|
|
40
|
-
|
129
|
+
*@error_detection_on = @opts[:error_detection_on]
|
130
|
+
@error_indicators = @opts[:error_indicators]
|
131
|
+
@on_error_do = @opts[:on_error_do]
|
41
132
|
|
42
|
-
@
|
43
|
-
Dir.chdir(working_directory)
|
133
|
+
@run_via = @opts[:run_via]
|
44
134
|
|
135
|
+
@working_directory = @opts[:working_directory]
|
136
|
+
@result = nil
|
45
137
|
end
|
46
138
|
|
47
139
|
private
|
48
140
|
|
141
|
+
# Find path to cmd
|
142
|
+
#
|
143
|
+
# @param [String] name
|
144
|
+
# Name of command. It accepts :cmd, 'cmd', 'rel_path/cmd' or
|
145
|
+
# '/fq_path/to/cmd'. When :cmd is used it searches 'search_paths' for the
|
146
|
+
# executable. Whenn 'cmd' is used it looks for cmd in local dir. The same
|
147
|
+
# happens when 'rel_path/cmd' is used. A full qualified path
|
148
|
+
# '/fq_path/to/cmd' is used as normal.
|
149
|
+
#
|
150
|
+
# @param [Array] search_paths
|
151
|
+
# Where to look for executables
|
152
|
+
#
|
153
|
+
# @return [String] fully qualified path to command
|
154
|
+
def resolve_path(name,*search_paths)
|
155
|
+
search_paths ||= ['/bin', '/usr/bin']
|
156
|
+
search_paths = search_paths.flatten
|
157
|
+
|
158
|
+
if name.kind_of? Symbol
|
159
|
+
path = search_paths.map{ |p| File.join(p, name.to_s) }.find {|p| File.exists? p } || ""
|
160
|
+
else
|
161
|
+
path = File.expand_path(name)
|
162
|
+
end
|
163
|
+
|
164
|
+
path
|
165
|
+
end
|
166
|
+
|
167
|
+
# Check if executable exists, if it's executable and is a file
|
168
|
+
#
|
169
|
+
# @raise [CommandExec::Exceptions::CommandNotFound] if command does not exist
|
170
|
+
# @raise [CommandExec::Exceptions::CommandNotExecutable] if command is not executable
|
171
|
+
# @raise [CommandExec::Exceptions::CommandIsNotAFile] if command is not a file
|
172
|
+
def check_path
|
173
|
+
unless exists?
|
174
|
+
@logger.fatal("Command '#{@name}' not found.")
|
175
|
+
raise Exceptions::CommandNotFound , "Command '#{@name}' not found."
|
176
|
+
end
|
177
|
+
|
178
|
+
unless executable?
|
179
|
+
@logger.fatal("Command '#{@name}' not executable.")
|
180
|
+
raise Exceptions::CommandNotExecutable , "Command '#{@name}' not executable."
|
181
|
+
end
|
182
|
+
|
183
|
+
unless file?
|
184
|
+
@logger.fatal("Command '#{@name}' not a file.")
|
185
|
+
raise Exceptions::CommandIsNotAFile, "Command '#{@name}' not a file."
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Set appropriate log level
|
49
190
|
def configure_logging
|
50
|
-
case @opts[:
|
191
|
+
case @opts[:lib_log_level]
|
51
192
|
when :debug
|
52
193
|
@logger.level = Logger::DEBUG
|
53
194
|
when :error
|
@@ -61,191 +202,200 @@ module CommandExec
|
|
61
202
|
when :warn
|
62
203
|
@logger.level = Logger::WARN
|
63
204
|
when :silent
|
64
|
-
@logger.
|
205
|
+
@logger.level = Logger::SILENT
|
65
206
|
else
|
66
|
-
|
207
|
+
@logger.level = Logger::INFO
|
67
208
|
end
|
68
|
-
end
|
69
209
|
|
70
|
-
|
71
|
-
#
|
72
|
-
# @param [Symbol] name Name of utility
|
73
|
-
# @return [Path] Returns the path to the binary of the binary
|
74
|
-
def resolve_cmd_name(name)
|
75
|
-
path=''
|
76
|
-
path = %x[which #{name} 2>/dev/null].chomp
|
210
|
+
@logger.debug "Logger configured with log level #{@logger.level}"
|
77
211
|
|
78
|
-
|
79
|
-
|
80
|
-
raise Exceptions::CommandNotFound
|
81
|
-
end
|
212
|
+
nil
|
213
|
+
end
|
82
214
|
|
83
|
-
|
215
|
+
public
|
216
|
+
|
217
|
+
#Is executable valid
|
218
|
+
def valid?
|
219
|
+
exists? and executable? and file?
|
84
220
|
end
|
85
221
|
|
86
|
-
#
|
87
|
-
#
|
88
|
-
# @return [
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
cmd += options.empty? ? "" : " #{options}"
|
93
|
-
cmd += parameter.empty? ? "" : " #{parameter}"
|
222
|
+
# Does the command exist?
|
223
|
+
#
|
224
|
+
# @return [True,False] result of check
|
225
|
+
def exists?
|
226
|
+
File.exists? @path
|
227
|
+
end
|
94
228
|
|
95
|
-
|
229
|
+
# Is the command executable
|
230
|
+
#
|
231
|
+
# @return [True,False] result of check
|
232
|
+
def executable?
|
233
|
+
File.executable? @path
|
96
234
|
end
|
97
235
|
|
98
|
-
|
236
|
+
# Is the provided string a file
|
237
|
+
#
|
238
|
+
# @return [True,False] result of check
|
239
|
+
def file?
|
240
|
+
File.file? @path
|
241
|
+
end
|
99
242
|
|
100
243
|
# Output the textual representation of a command
|
101
|
-
# public alias for build_cmd_string
|
102
244
|
#
|
103
245
|
# @return [String] command in text form
|
104
|
-
def
|
105
|
-
|
246
|
+
def to_s
|
247
|
+
cmd = ''
|
248
|
+
cmd += @path
|
249
|
+
cmd += @options.blank? ? "" : " #{@options}"
|
250
|
+
cmd += @parameter.blank? ? "" : " #{@parameter}"
|
251
|
+
|
252
|
+
@logger.debug cmd
|
253
|
+
|
254
|
+
cmd
|
106
255
|
end
|
107
256
|
|
108
257
|
# Run the program
|
109
258
|
#
|
259
|
+
# @raise [CommandExec::Exceptions::CommandExecutionFailed] if an error
|
260
|
+
# occured and `command_exec` should raise an exception in the case of an
|
261
|
+
# error.
|
262
|
+
# @throw [:command_execution_failed] if an error
|
263
|
+
# occured and `command_exec` should throw an error (which you can catch)
|
264
|
+
# in the case of an error
|
110
265
|
def run
|
266
|
+
process = CommandExec::Process.new(:lib_logger => @logger)
|
267
|
+
process.log_file = @log_file if @log_file
|
268
|
+
process.status = :success
|
111
269
|
|
112
|
-
|
113
|
-
_stderr = ''
|
114
|
-
|
115
|
-
status = POpen4::popen4(build_cmd_string) do |stdout, stderr, stdin, pid|
|
116
|
-
_stdout = stdout.read.strip
|
117
|
-
_stderr = stderr.read.strip
|
118
|
-
end
|
270
|
+
check_path
|
119
271
|
|
272
|
+
process.start_time = Time.now
|
120
273
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
)
|
274
|
+
case @run_via
|
275
|
+
when :open3
|
276
|
+
Open3::popen3(to_s, :chdir => @working_directory) do |stdin, stdout, stderr, wait_thr|
|
277
|
+
process.stdout = stdout.readlines.map(&:chomp)
|
278
|
+
process.stderr = stderr.readlines.map(&:chomp)
|
279
|
+
process.pid = wait_thr.pid
|
280
|
+
process.return_code = wait_thr.value.exitstatus
|
281
|
+
end
|
282
|
+
when :system
|
283
|
+
Dir.chdir(@working_directory) do
|
284
|
+
system(to_s)
|
285
|
+
process.stdout = []
|
286
|
+
process.stderr = []
|
287
|
+
process.pid = $?.pid
|
288
|
+
process.return_code = $?.exitstatus
|
289
|
+
end
|
138
290
|
else
|
139
|
-
|
291
|
+
Open3::popen3(to_s, :chdir => @working_directory) do |stdin, stdout, stderr, wait_thr|
|
292
|
+
process.stdout = stdout.readlines.map(&:chomp)
|
293
|
+
process.stderr = stderr.readlines.map(&:chomp)
|
294
|
+
process.pid = wait_thr.pid
|
295
|
+
process.return_code = wait_thr.value.exitstatus
|
296
|
+
end
|
140
297
|
end
|
141
298
|
|
142
|
-
|
299
|
+
process.end_time = Time.now
|
143
300
|
|
144
|
-
|
145
|
-
|
301
|
+
if @error_detection_on.include?(:return_code)
|
302
|
+
if not @error_indicators[:allowed_return_code].include? process.return_code or
|
303
|
+
@error_indicators[:forbidden_return_code].include? process.return_code
|
146
304
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
content = StringIO.new
|
305
|
+
@logger.debug "Error detection on return code found an error"
|
306
|
+
process.status = :failed
|
307
|
+
process.reason_for_failure = :return_code
|
308
|
+
end
|
309
|
+
end
|
153
310
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
311
|
+
if @error_detection_on.include?(:stderr) and not process.status == :failed
|
312
|
+
if error_occured?( @error_indicators[:forbidden_words_in_stderr], @error_indicators[:allowed_words_in_stderr], process.stderr)
|
313
|
+
@logger.debug "Error detection on stderr found an error"
|
314
|
+
process.status = :failed
|
315
|
+
process.reason_for_failure = :stderr
|
316
|
+
end
|
159
317
|
end
|
160
|
-
end
|
161
318
|
|
162
|
-
|
163
|
-
|
319
|
+
if @error_detection_on.include?(:stdout) and not process.status == :failed
|
320
|
+
if error_occured?( @error_indicators[:forbidden_words_in_stdout], @error_indicators[:allowed_words_in_stdout], process.stdout)
|
321
|
+
@logger.debug "Error detection on stdout found an error"
|
322
|
+
process.status = :failed
|
323
|
+
process.reason_for_failure = :stdout
|
324
|
+
end
|
325
|
+
end
|
164
326
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
return true
|
173
|
-
end
|
174
|
-
end
|
327
|
+
if @error_detection_on.include?(:log_file) and not process.status == :failed
|
328
|
+
if error_occured?( @error_indicators[:forbidden_words_in_log_file], @error_indicators[:allowed_words_in_log_file], process.log_file)
|
329
|
+
@logger.debug "Error detection on log file found an error"
|
330
|
+
process.status = :failed
|
331
|
+
process.reason_for_failure = :log_file
|
332
|
+
end
|
333
|
+
end
|
175
334
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
if error_in_exec == true
|
191
|
-
result << '================== LOGFILE ================== '
|
192
|
-
result << logfile if logfile.empty? == false
|
193
|
-
result << '================== STDOUT ================== '
|
194
|
-
result << stdout if stdout.empty? == false
|
195
|
-
result << '================== STDERR ================== '
|
196
|
-
result << stderr if stderr.empty? == false
|
197
|
-
elsif error_in_stdout == true
|
198
|
-
result << '================== STDOUT ================== '
|
199
|
-
result << stdout
|
335
|
+
@logger.debug "Result of command run #{process.status}"
|
336
|
+
|
337
|
+
@result = process
|
338
|
+
if process.status == :failed
|
339
|
+
case @on_error_do
|
340
|
+
when :nothing
|
341
|
+
#nothing
|
342
|
+
when :raise_error
|
343
|
+
raise CommandExec::Exceptions::CommandExecutionFailed, "An error occured. Please check for reason via command.reason_for_failure and/or command.stdout, comand.stderr, command.log_file, command.return_code"
|
344
|
+
when :throw_error
|
345
|
+
throw :command_execution_failed
|
346
|
+
else
|
347
|
+
#nothing
|
348
|
+
end
|
200
349
|
end
|
201
|
-
|
202
|
-
result
|
203
350
|
end
|
204
351
|
|
205
|
-
# Find error in
|
206
|
-
#
|
352
|
+
# Find error in data
|
353
|
+
#
|
354
|
+
# @param [Array,String] forbidden_word
|
355
|
+
# what are the forbidden words which indidcate an error
|
356
|
+
#
|
357
|
+
# @param [Array,String] exception
|
358
|
+
# Is there any exception from that forbidden words, maybe a string
|
359
|
+
# which contains the forbidden word, but is no error?
|
360
|
+
#
|
361
|
+
# @param [Array,String] data
|
362
|
+
# Where to look for errors.
|
363
|
+
#
|
207
364
|
# @return [Boolean] Returns true if it finds an error
|
208
|
-
def
|
209
|
-
return false if keywords.empty? or not keywords.is_a? Array
|
210
|
-
return false if string.nil? or not string.is_a? String
|
211
|
-
|
365
|
+
def error_occured?(forbidden_word, exception, data )
|
212
366
|
error_found = false
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
367
|
+
*forbidden_word = forbidden_word
|
368
|
+
*exception = exception
|
369
|
+
*data = data
|
370
|
+
|
371
|
+
return false if forbidden_word.blank?
|
372
|
+
return false if data.blank?
|
373
|
+
|
374
|
+
forbidden_word.each do |word|
|
375
|
+
data.each do |line|
|
376
|
+
line.strip!
|
377
|
+
|
378
|
+
#line includes word -> error
|
379
|
+
#exception does not include line/substring of line -> error, if
|
380
|
+
# includes line/substring of line -> no error
|
381
|
+
if line.include? word and exception.find{ |e| line[e] }.blank?
|
382
|
+
error_found = true
|
383
|
+
break
|
384
|
+
end
|
217
385
|
end
|
218
386
|
end
|
219
387
|
|
220
388
|
error_found
|
221
389
|
end
|
222
390
|
|
223
|
-
#
|
224
|
-
#
|
225
|
-
# @param [Boolean] run_successful true if a positive message should be returned
|
226
|
-
# @param [Array] msg Message which should be returned
|
227
|
-
def message(run_successful, *msg)
|
228
|
-
|
229
|
-
message = []
|
230
|
-
if run_successful
|
231
|
-
message << 'OK'.green.bold
|
232
|
-
else
|
233
|
-
message << 'FAILED'.red.bold
|
234
|
-
message.concat msg.flatten
|
235
|
-
end
|
236
|
-
|
237
|
-
message.join("\n")
|
238
|
-
end
|
239
|
-
|
240
|
-
# Constructur to initiate a new command and run it later
|
391
|
+
# Run a command
|
241
392
|
#
|
242
393
|
# @see #initialize
|
243
|
-
def
|
394
|
+
def self.execute(name,opts={})
|
244
395
|
command = new(name,opts)
|
245
396
|
command.run
|
246
397
|
|
247
398
|
command
|
248
399
|
end
|
249
|
-
|
250
400
|
end
|
251
401
|
end
|