command_exec 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|