teuton 3.1.0 → 3.2.1

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/bin/teuton +2 -3
  4. data/docs/commands/readme.md +1 -1
  5. data/docs/devel/todo.md +87 -44
  6. data/docs/dsl/send.md +3 -3
  7. data/docs/dsl/show.md +2 -2
  8. data/docs/install/t-node.md +4 -14
  9. data/docs/learn/18-log.md +55 -20
  10. data/docs/tutorial/es/nginx/README.md +4 -4
  11. data/lib/teuton/case/action.rb +31 -0
  12. data/lib/teuton/case/case.rb +8 -12
  13. data/lib/teuton/case/close.rb +1 -1
  14. data/lib/teuton/case/dsl/expect.rb +10 -11
  15. data/lib/teuton/case/dsl/expect_sequence.rb +1 -2
  16. data/lib/teuton/case/dsl/macro.rb +3 -2
  17. data/lib/teuton/case/dsl/run.rb +4 -5
  18. data/lib/teuton/case/dsl/run_script.rb +3 -3
  19. data/lib/teuton/case/dsl/send.rb +2 -18
  20. data/lib/teuton/case/dsl/target.rb +6 -4
  21. data/lib/teuton/case/dsl/upload.rb +2 -5
  22. data/lib/teuton/case/dsl/weight.rb +3 -3
  23. data/lib/teuton/case/execute/execute_base.rb +4 -4
  24. data/lib/teuton/case/execute/execute_local.rb +2 -2
  25. data/lib/teuton/case/execute/execute_manager.rb +6 -6
  26. data/lib/teuton/case/execute/execute_ssh.rb +6 -6
  27. data/lib/teuton/case/execute/execute_telnet.rb +4 -4
  28. data/lib/teuton/case/play.rb +7 -8
  29. data/lib/teuton/case_manager/case_manager.rb +4 -4
  30. data/lib/teuton/case_manager/dsl.rb +7 -10
  31. data/lib/teuton/case_manager/export_manager.rb +6 -12
  32. data/lib/teuton/case_manager/ext/check_cases.rb +1 -1
  33. data/lib/teuton/cli/app.rb +123 -0
  34. data/lib/teuton/cli/run.rb +38 -0
  35. data/lib/teuton/errors.rb +94 -0
  36. data/lib/teuton/readme/action.rb +29 -0
  37. data/lib/teuton/readme/dsl/all.rb +7 -5
  38. data/lib/teuton/readme/dsl/expect.rb +3 -3
  39. data/lib/teuton/readme/dsl/run.rb +5 -11
  40. data/lib/teuton/readme/main.rb +13 -6
  41. data/lib/teuton/readme/readme.rb +12 -15
  42. data/lib/teuton/report/formatter/base_formatter.rb +2 -2
  43. data/lib/teuton/report/formatter/default/txt.rb +2 -2
  44. data/lib/teuton/report/formatter/formatter.rb +4 -13
  45. data/lib/teuton/report/formatter/resume/txt.rb +2 -2
  46. data/lib/teuton/utils/config_file_reader.rb +4 -12
  47. data/lib/teuton/utils/logger.rb +15 -10
  48. data/lib/teuton/utils/name_file_finder.rb +3 -11
  49. data/lib/teuton/version.rb +1 -1
  50. data/lib/teuton.rb +6 -11
  51. metadata +6 -2
  52. data/lib/teuton/cli.rb +0 -120
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DSL
4
- # * send, tempfile, tempdir, remote_tempdir, remote_tempfile
4
+ # * send, tempdir, remote_tempdir
5
5
  def send(logfile, args = {})
6
6
  return if skip?
7
7
 
@@ -38,7 +38,7 @@ module DSL
38
38
  logfile.write "#{msg}\n"
39
39
  logfile.flush
40
40
  rescue => e
41
- msg = Rainbow("==> [FAIL] Case #{get(:tt_members)}: 'scp #{localfilepath}' to #{remotefilepath}").red
41
+ msg = Rainbow("==> [FAIL] Case #{get(:tt_members)}: 'scp #{localfilepath}' to #{remotefilepath}").yellow
42
42
  msg += "\n--> [ERROR] #{e}"
43
43
  verboseln(msg)
44
44
  logfile.write "#{msg}\n"
@@ -46,26 +46,10 @@ module DSL
46
46
  end
47
47
  end
48
48
 
49
- def tempfile(input = nil)
50
- return @action[:tempfile] if input.nil?
51
-
52
- name = input
53
- name = "teuton.tmp" if input == :default
54
-
55
- @action[:tempfile] = File.join(@tempdir, name)
56
- @action[:remote_tempfile] = File.join(remote_tempdir, name)
57
-
58
- @action[:tempfile]
59
- end
60
-
61
49
  def tempdir
62
50
  @tempdir
63
51
  end
64
52
 
65
- def remote_tempfile
66
- @action[:remote_tempfile]
67
- end
68
-
69
53
  def remote_tempdir
70
54
  File.join("/", "tmp") # TODO: Remove this?
71
55
  end
@@ -3,14 +3,16 @@
3
3
  # DSL#target
4
4
  module DSL
5
5
  def readme(_text)
6
- # Usefull only for "teuton reamde" command action.
6
+ # Usefull when running.
7
+ # It's works only with "teuton reamde" command action.
7
8
  end
8
9
 
9
10
  def target(desc, args = {})
10
- @action[:description] = desc.to_s
11
- @action[:asset] = args[:asset].to_s if args[:asset]
11
+ @action.description = desc.to_s
12
+ @action.asset = args[:asset].to_s if args[:asset]
12
13
  w = args[:weight] || 1.0
13
14
  weight(w)
14
15
  end
15
- alias_method :goal, :target
16
+
17
+ alias_method :goal, :target # DEPRECATE
16
18
  end
@@ -15,10 +15,7 @@ module DSL
15
15
  end
16
16
 
17
17
  def upload_one(localpath, args = {})
18
- if args[:to].nil?
19
- Logger.err("ERROR upload requires to: XXX")
20
- exit 1
21
- end
18
+ raise ArgumentError, "upload requires 'to: XXX'" if args[:to].nil?
22
19
 
23
20
  host = get_host(args[:to])
24
21
  if host.protocol == "ssh"
@@ -35,7 +32,7 @@ module DSL
35
32
  verbose(Rainbow("!").green)
36
33
  end
37
34
  elsif host.protocol != "local"
38
- msg = Rainbow("==> [ERROR] upload: Incorrect protocol(#{host.protocol})").red
35
+ msg = Rainbow("==> [ERROR] upload: Incorrect protocol(#{host.protocol})").bright.red
39
36
  verboseln(msg)
40
37
  end
41
38
  end
@@ -2,11 +2,11 @@ module DSL
2
2
  def weight(value = nil)
3
3
  # Set weight value for the action
4
4
  if value.nil?
5
- @action[:weight]
5
+ @action.weight
6
6
  elsif value == :default
7
- @action[:weight] = 1.0
7
+ @action.weight = 1.0
8
8
  else
9
- @action[:weight] = value.to_f
9
+ @action.weight = value.to_f
10
10
  end
11
11
  end
12
12
  end
@@ -1,7 +1,7 @@
1
1
  class ExecuteBase
2
2
  def initialize(parent)
3
3
  @parent = parent
4
- # READ: @config, cmd = action[:command]
4
+ # READ: @config, cmd = action.command
5
5
  # WRITE: @action, @result, @session
6
6
  end
7
7
 
@@ -46,9 +46,9 @@ class ExecuteBase
46
46
  begin
47
47
  text = ec.convert(text)
48
48
  rescue => e
49
- puts "[ERROR] ExecuteBase: #{e}"
50
- puts "[ERROR] Declare text encoding. Example:"
51
- puts "[ERROR] run 'command', on: :host, encoding: 'ISO-8859-1'"
49
+ warn Rainbow("[ERROR] ExecuteBase: #{e}").bright.red
50
+ warn Rainbow("[ERROR] Declare text encoding. Example:").white
51
+ warn Rainbw("[ERROR] run 'command', on: :host, encoding: 'ISO-8859-1'").white
52
52
  end
53
53
 
54
54
  text.split("\n").compact
@@ -8,8 +8,8 @@ class ExecuteLocal < ExecuteBase
8
8
  include Verbose
9
9
 
10
10
  def call
11
- action[:conn_type] = :local
12
- response = my_execute(action[:command], action[:encoding])
11
+ action.conn_type = :local
12
+ response = my_execute(action.command, action.encoding)
13
13
  result.exitcode = response[:exitcode]
14
14
  result.content = response[:content]
15
15
  end
@@ -5,7 +5,7 @@ require_relative "execute_telnet"
5
5
  class ExecuteManager
6
6
  def initialize(parent)
7
7
  @parent = parent
8
- # READ: @config, cmd = action[:command]
8
+ # READ: @config, cmd = action.command
9
9
  # WRITE: @action, @result, @session
10
10
  # my_execute, encode_and_split methods?
11
11
  end
@@ -13,7 +13,7 @@ class ExecuteManager
13
13
  def call(host)
14
14
  start_time = Time.now
15
15
  run_on(host)
16
- action[:duration] = (Time.now - start_time).round(3)
16
+ action.duration = (Time.now - start_time).round(3)
17
17
  end
18
18
 
19
19
  private
@@ -34,16 +34,16 @@ class ExecuteManager
34
34
  protocol = config.get(:"#{host}_protocol")
35
35
  ip = config.get(:"#{host}_ip")
36
36
 
37
- if protocol.to_s.downcase == "local" || host.to_s == "localhost"
37
+ if protocol.to_s.casecmp("local").zero? || host.to_s == "localhost"
38
38
  # Protocol force => local
39
39
  ExecuteLocal.new(@parent).call
40
- elsif protocol.to_s.downcase == "ssh"
40
+ elsif protocol.to_s.casecmp("ssh").zero?
41
41
  # Protocol force => ssh
42
42
  ExecuteSSH.new(@parent).call(host)
43
- elsif protocol.to_s.downcase == "telnet"
43
+ elsif protocol.to_s.casecmp("telnet").zero?
44
44
  # Protocol force => telnet
45
45
  ExecuteTelnet.new(@parent).call(host)
46
- elsif ip.to_s.downcase == "localhost" || ip.to_s.include?("127.0.0.")
46
+ elsif ip.to_s.casecmp("localhost").zero? || ip.to_s.include?("127.0.0.")
47
47
  # run_cmd_localhost
48
48
  ExecuteLocal.new(@parent).call
49
49
  elsif ip == "NODATA"
@@ -6,7 +6,7 @@ require_relative "execute_base"
6
6
 
7
7
  class ExecuteSSH < ExecuteBase
8
8
  def call(input_hostname)
9
- action[:conn_type] = :ssh
9
+ action.conn_type = :ssh
10
10
  hostname = input_hostname.to_s
11
11
  ip = config.get(:"#{hostname}_ip").to_s
12
12
  username = config.get(:"#{hostname}_username").to_s
@@ -20,14 +20,14 @@ class ExecuteSSH < ExecuteBase
20
20
  ip2 = ip
21
21
  username2 = username
22
22
  password2 = password
23
- command2 = action[:command]
23
+ command2 = action.command
24
24
  hostname = config.get(:"#{hostname}_route")
25
25
  ip = config.get(:"#{hostname}_ip").to_s
26
26
  username = config.get(:"#{hostname}_username").to_s
27
27
  password = config.get(:"#{hostname}_password").to_s
28
28
  ostype = config.get(:"#{hostname}_ostype").to_s
29
29
 
30
- action[:command] = if ostype.downcase.start_with? "win"
30
+ action.command = if ostype.downcase.start_with? "win"
31
31
  "echo y | plink #{username2}@#{ip2} -ssh -pw #{password2} \"#{command2}\""
32
32
  else
33
33
  "sshpass -p #{password2} #{username2}@#{ip2} #{command2}"
@@ -49,7 +49,7 @@ class ExecuteSSH < ExecuteBase
49
49
  )
50
50
  end
51
51
  if sessions[hostname].instance_of? Net::SSH::Connection::Session
52
- text = sessions[hostname].exec!(action[:command])
52
+ text = sessions[hostname].exec!(action.command)
53
53
  exitcode = text.exitstatus
54
54
  else
55
55
  text = "TEUTON_ERROR_SSH_NO_CONNECTION"
@@ -84,9 +84,9 @@ class ExecuteSSH < ExecuteBase
84
84
  text = "TEUTON_ERROR_SSH"
85
85
  exitcode = -1
86
86
  log("[#{e.class}] SSH on <#{username}@#{ip}>" \
87
- " exec: #{action[:command]}", :error)
87
+ " exec: #{action.command}", :error)
88
88
  end
89
89
  result.exitcode = exitcode
90
- result.content = encode_and_split(action[:encoding], text)
90
+ result.content = encode_and_split(action.encoding, text)
91
91
  end
92
92
  end
@@ -4,7 +4,7 @@ require_relative "execute_base"
4
4
 
5
5
  class ExecuteTelnet < ExecuteBase
6
6
  def call(input_hostname)
7
- action[:conn_type] = :telnet
7
+ action.conn_type = :telnet
8
8
  hostname = input_hostname.to_s
9
9
  ip = config.get((hostname + "_ip").to_sym)
10
10
  port = config.get((hostname + "_port").to_sym)
@@ -31,7 +31,7 @@ class ExecuteTelnet < ExecuteBase
31
31
  password = config.get((hostname + "_password").to_sym).to_s
32
32
  h.login(username, password)
33
33
  end
34
- h.cmd(action[:command]) { |i| text << i }
34
+ h.cmd(action.command) { |i| text << i }
35
35
  h.close
36
36
  sessions[hostname] = :ok
37
37
  exitcode = 0
@@ -51,11 +51,11 @@ class ExecuteTelnet < ExecuteBase
51
51
  sessions[hostname] = :nosession
52
52
  conn_status[hostname] = :error
53
53
  log(" ExceptionType=<#{e.class}> doing telnet on <#{username}@#{ip}>" \
54
- " exec: #{action[:command]}", :error)
54
+ " exec: #{action.command}", :error)
55
55
  log(" └── username=<#{username}>, password=<#{password}>," \
56
56
  " ip=<#{ip}>, HOSTID=<#{hostname}>", :warn)
57
57
  end
58
58
  result.exitcode = exitcode
59
- result.content = encode_and_split(action[:encoding], text)
59
+ result.content = encode_and_split(action.encoding, text)
60
60
  end
61
61
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  class Case
4
4
  # READ: sessions, config, groups, action, report
5
- # TODO: groups from Project or from Case???
6
5
  def play
7
6
  if skip?
8
7
  verbose Rainbow("S").green
@@ -25,18 +24,18 @@ class Case
25
24
 
26
25
  def play_groups_in_sequence
27
26
  verboseln "\n==> Running case [#{@config.get(:tt_members)}]" if get(:tt_sequence) == true
28
- @groups.each do |t|
29
- @action[:groupname] = t[:name]
30
- instance_eval(&t[:block])
27
+ @groups.each do |group|
28
+ @action.groupname = group[:name]
29
+ instance_eval(&group[:block])
31
30
  end
32
31
  end
33
32
 
34
33
  def play_groups_in_parallel
35
34
  verboseln "Running case [#{@config.get(:tt_members)}]"
36
- @groups.each do |t|
37
- verbose "* Processing [#{t[:name]}] "
38
- @action[:groupname] = t[:name]
39
- instance_eval(&t[:block])
35
+ @groups.each do |group|
36
+ verbose "* Processing [#{group[:name]}] "
37
+ @action.groupname = group[:name]
38
+ instance_eval(&group[:block])
40
39
  verbose "\n"
41
40
  end
42
41
  verboseln "\n"
@@ -29,11 +29,11 @@ class CaseManager
29
29
  check_cases!
30
30
  instance_eval(&block)
31
31
  # Run export if user pass option command "--export=FORMAT"
32
- i = Project.value[:options]["export"]
33
- export(format: i.to_sym) unless i.nil?
32
+ option = Project.value[:options]["export"]
33
+ export(format: option.to_sym) unless option.nil?
34
34
  # Accept "configfile" param REVISE There exists?
35
- i = Project.value[:options]["configfile"]
36
- export(format: i.to_sym) unless i.nil?
35
+ option = Project.value[:options]["configfile"]
36
+ export(format: option.to_sym) unless option.nil?
37
37
  # TODO: Export Stats
38
38
  # StatsManager.new.call(@cases)
39
39
  end
@@ -13,19 +13,19 @@ end
13
13
  alias def_macro define_macro
14
14
  alias defmacro define_macro
15
15
 
16
- # Define a group of [target/run/expect]s
17
- # @param name (String) Group name
18
- # @param block (Block) Tests code
19
16
  def group(name, &block)
20
17
  Project.value[:groups] << { name: name, block: block }
21
18
  end
22
19
  alias task group
23
20
 
24
21
  def play(&block)
25
- # Start test
26
22
  CaseManager.new.play(&block)
27
23
  end
28
- alias start play
24
+
25
+ def start(...)
26
+ Logger.warn "[DEPRECATED] Replace 'start do' by `play do'"
27
+ play(...)
28
+ end
29
29
 
30
30
  def use(filename)
31
31
  filename += ".rb"
@@ -36,10 +36,7 @@ def use(filename)
36
36
  begin
37
37
  require_relative findfiles.first
38
38
  Project.value[:uses] << File.basename(findfiles.first)
39
- rescue => e
40
- puts "[ERROR] case_manager/dsl: #{e}"
41
- puts "[ERROR] File not found!"
42
- puts "[ERROR] Fix line <use '#{filename}'>"
43
- exit 1
39
+ rescue StandardError => e
40
+ raise Teuton::CaseManagerDslError.new(e, filename)
44
41
  end
45
42
  end
@@ -14,23 +14,17 @@ class ExportManager
14
14
  # @param cases (Array)
15
15
  # @param input (Hash) Selected export options
16
16
  def call(main_report, cases, args)
17
- if args.class != Hash
18
- puts Rainbow("[ERROR] ExportManager: argument error!").red
19
- puts Rainbow("[ERROR] Fix line <export #{args}>").red
20
- puts Rainbow("[ERROR] Replace by <export format: 'txt'>").red
21
- puts ""
22
- exit 1
23
- end
17
+ raise Teuton::ExportArgumentError, args if args.class != Hash
24
18
 
25
19
  # Step 1: Validate options
26
20
  options = strings2symbols(args)
27
21
  options[:format] = Project.value[:format] if options[:format].nil?
28
22
 
29
23
  unless Formatter.available_formats.include? options[:format]
30
- puts Rainbow("[WARN] ExportManager: Unkown format!").yellow.bright
31
- puts Rainbow("[WARN] Fix line <export format: #{options[:format]}>").yellow.bright
32
- puts Rainbow("[INFO] Available formats: #{Formatter.available_formats.join(", ")}.").white.bright
33
- puts Rainbow("[INFO] Using default format <txt>.").white.bright
24
+ Logger.warn "ExportManager: Unkown format!"
25
+ Logger.warn "Fix line <export format: #{options[:format]}>"
26
+ Logger.warn "Available formats: #{Formatter.available_formats.join(", ")}."
27
+ Logger.warn "Using default format <txt>."
34
28
  options[:format] = :txt
35
29
  end
36
30
 
@@ -84,7 +78,7 @@ class ExportManager
84
78
  "%<hour>02d%<min>02d%<sec>02d", data)
85
79
  preserve_dir = File.join(srcdir, subdir)
86
80
 
87
- puts "[INFO] Preserving files => #{preserve_dir}"
81
+ Logger.info "Preserving files => #{preserve_dir}"
88
82
  FileUtils.mkdir(preserve_dir)
89
83
  Dir.glob(File.join(srcdir, "**.*")).each { |file| FileUtils.cp(file, preserve_dir) }
90
84
  end
@@ -69,7 +69,7 @@ module CheckCasesExtension
69
69
  # 3) And order to build hall of fame
70
70
  def close_reports_for_all_cases(uniques)
71
71
  threads = []
72
- @cases.each { |c| threads << Thread.new { c.close uniques } }
72
+ @cases.each { |c| threads << Thread.new { c.close(uniques) } }
73
73
  threads.each(&:join)
74
74
 
75
75
  HallOfFame.new(@cases).call
@@ -0,0 +1,123 @@
1
+ require "thor"
2
+ require_relative "../version"
3
+ require_relative "../../teuton"
4
+
5
+ module Teuton
6
+ module CLI
7
+ class App < Thor
8
+ map ["h", "-h", "--help"] => "help"
9
+
10
+ map ["n", "-n", "--new"] => "new"
11
+ desc "new DIRECTORY", "Create skeleton for a new project"
12
+ long_desc <<-LONGDESC
13
+ Create files for a new project.
14
+ LONGDESC
15
+ def new(projectpath)
16
+ Teuton.create(projectpath)
17
+ end
18
+
19
+ map ["c", "-c", "--check"] => "check"
20
+ option :onlyconfig, type: :boolean
21
+ option :color, type: :boolean
22
+ option :cname, type: :string
23
+ option :cpath, type: :string
24
+ desc "check [OPTIONS] DIRECTORY", "Check test and config file content"
25
+ long_desc <<~LONGDESC
26
+
27
+ (1) teuton check PATH/TO/DIR , Check content of start.rb and config.yaml files.
28
+
29
+ (2) teuton check PATH/TO/DIR --cname=demo , Check content of start.rb and demo.yaml files:
30
+
31
+ (3) teuton check PATH/TO/DIR --onlyconfig , Only show config file recomendations
32
+
33
+ (4) teuton check PATH/TO/DIR/foo.rb , Check content of foo.rb and foo.yaml files.
34
+
35
+ (5) teuton check PATH/TO/DIR/foo.rb --cname=demo , Check content of foo.rb and demo.yaml files.
36
+
37
+ LONGDESC
38
+ def check(projectpath)
39
+ Teuton.check(projectpath, options)
40
+ end
41
+
42
+ map ["co", "-co", "--config"] => "config"
43
+ desc "config [OPTIONS] DIRECTORY", "Suggest configuration."
44
+ long_desc <<~LONGDESC
45
+ config DIRECTORY, "Suggest the content of the configuration file based on the test"
46
+ LONGDESC
47
+ def config(projectpath)
48
+ # Check Test: suggest the content of the configuration file based on the test
49
+ opt = {"onlyconfig" => true}
50
+ Teuton.check(projectpath, opt)
51
+ end
52
+
53
+ map ["--readme"] => "readme"
54
+ option :lang, type: :string
55
+ desc "readme DIRECTORY", "Show README extracted from test contents"
56
+ long_desc <<-LONGDESC
57
+
58
+ (1) teuton readme foo
59
+ , Create README.md from foo/start.rb.
60
+
61
+ (2) teuton readme foo/demo.rb
62
+ , Create README.md from foo/demo.rb.
63
+ LONGDESC
64
+
65
+ def readme(projectpath)
66
+ Teuton.readme(projectpath, options)
67
+ end
68
+
69
+ map ["--run", "run"] => "play"
70
+ option :export, type: :string
71
+ option :cname, type: :string
72
+ option :cpath, type: :string
73
+ option :case, type: :string
74
+ option :color, type: :boolean
75
+ option :quiet, type: :boolean
76
+ desc "[run] [OPTIONS] DIRECTORY", "Run test from directory"
77
+ long_desc <<-LONGDESC
78
+ This function execute challenge from specified directory.
79
+ By default, show progress on the screen.
80
+
81
+ Let's see others options:
82
+
83
+ (1) teuton foo, run challenge from foo/start.rb with foo/config.yaml config file.
84
+
85
+ (2) teuton run foo, same as (1).
86
+
87
+ (3) teuton run --export=json foo, run challenge and export using json format.
88
+ Others output formats availables are: txt, html, yaml, json and colored_text.
89
+
90
+ (4) teuton run --cname=demo foo, run challenge from foo/start.rb with foo/demo.yaml config file.
91
+
92
+ (5) teuton foo/demo.rb, Run challenge from foo/demo.rb with foo/demo.yaml config file.
93
+
94
+ LONGDESC
95
+ def play(filepath)
96
+ Teuton.run(filepath, options)
97
+ end
98
+
99
+ map ["v", "-v", "--version"] => "version"
100
+ desc "version", "Show the program version"
101
+ def version
102
+ puts "#{Teuton::APPNAME} version #{Teuton::VERSION}"
103
+ end
104
+
105
+ ##
106
+ # These inputs are equivalents:
107
+ # * teuton dir/foo
108
+ # * teuton run dir/foo
109
+ def method_missing(method, *_args, &_block)
110
+ play(method.to_s)
111
+ end
112
+
113
+ def respond_to_missing?(method_name, include_private = false)
114
+ # Respond to missing methods name
115
+ super
116
+ end
117
+
118
+ def self.exit_on_failure?
119
+ true
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "app"
4
+
5
+ module Teuton::CLI
6
+ def self.run(argv)
7
+ begin
8
+ Teuton::CLI::App.start(argv)
9
+ exit 0
10
+ rescue Interrupt
11
+ puts "\nExecution cancelled by the user."
12
+ exit 130
13
+ rescue Teuton::CaseManagerDslError => e
14
+ lines = e.message.split("\n")
15
+ head = lines.shift
16
+ warn "Error | #{head}"
17
+ lines.each { |line| warn Rainbow("Error| #{line}").white }
18
+ rescue Teuton::ConfigFileError => e
19
+ lines = e.message.split("\n")
20
+ head = lines.shift
21
+ warn "Error | #{head}"
22
+ lines.each { |line| warn Rainbow("Error| #{line}").white }
23
+ rescue Teuton::MainScriptNotFoundError => e
24
+ warn "Error | #{e.message}"
25
+ rescue Teuton::Error => e
26
+ warn "Controlled error: #{e.message}"
27
+ e.backtrace&.first(3)&.each do |line|
28
+ warn Rainbow("Error | #{line}").white
29
+ end
30
+ rescue => e
31
+ warn "Unexpected error: #{e.message}"
32
+ raise e if ENV["DEBUG"]
33
+
34
+ e.backtrace&.first(3)&.each { |line| warn Rainbow("Error | #{line}").white }
35
+ end
36
+ exit 1
37
+ end
38
+ end
@@ -0,0 +1,94 @@
1
+ module Teuton
2
+ class Error < StandardError; end
3
+
4
+ class CaseManagerDslError < Error
5
+ attr_reader :original_error, :filename
6
+
7
+ def initialize(original_error, filename)
8
+ @original_error = original_error
9
+ @filename = filename
10
+ super(build_message)
11
+ end
12
+
13
+ private
14
+
15
+ def build_message
16
+ <<~MSG.strip
17
+ case_manager/dsl: #{@original_error}
18
+ File not found! Fix line <use '#{@filename}'>
19
+ MSG
20
+ end
21
+ end
22
+
23
+ class ConfigurationError < Error; end
24
+
25
+ class ConfigFileError < Error
26
+ attr_reader :filepath, :original_error
27
+
28
+ def initialize(filepath, original_error)
29
+ @filepath = filepath
30
+ @original_error = original_error
31
+ super(build_message)
32
+ end
33
+
34
+ private
35
+
36
+ def build_message
37
+ <<~MSG.strip
38
+ Reading configurations file:
39
+ - File : #{@filepath}
40
+ - Type : #{@original_error}
41
+ MSG
42
+ end
43
+ end
44
+
45
+ class InputError < Error; end
46
+ class ExecutionError < Error; end
47
+
48
+ class ExportArgumentError < Error
49
+ attr_reader :args
50
+
51
+ def initialize(args)
52
+ @args = args
53
+ super(build_message)
54
+ end
55
+
56
+ private
57
+
58
+ def build_message
59
+ <<~MSG.strip
60
+ ExportManager: argument error!
61
+ - Fix line <export #{@args}>
62
+ - Replace by <export format: 'txt'>
63
+ MSG
64
+ end
65
+ end
66
+
67
+ class MainScriptNotFoundError < Error
68
+ attr_reader :script_path
69
+
70
+ def initialize(script_path)
71
+ @script_path = script_path
72
+ super(build_message)
73
+ end
74
+
75
+ private
76
+
77
+ def build_message
78
+ <<~MSG.strip
79
+ Main script file not found!
80
+ - File: #{script_path}
81
+ MSG
82
+ end
83
+ end
84
+
85
+ class UnexpectedSequenceTypeError < Error
86
+ attr_reader :received_class
87
+
88
+ def initialize(value_class)
89
+ super("expect_sequence #{value_class}")
90
+ end
91
+ end
92
+
93
+ class UploadArgumentError < ArgumentError; end
94
+ end
@@ -0,0 +1,29 @@
1
+ class Readme
2
+ class Action
3
+ attr_reader :id
4
+ attr_accessor :description, :weight, :readme, :host, :exec
5
+
6
+ def initialize
7
+ @id = 0
8
+ @description = nil
9
+ @host = nil
10
+ @readme = []
11
+ end
12
+
13
+ def reset
14
+ @id += 1
15
+ @description = nil
16
+ @host = nil
17
+ @readme = []
18
+ self
19
+ end
20
+
21
+ def to_h
22
+ {
23
+ id: id,
24
+ description: description, weight: weight, readme: readme,
25
+ host: host, exec: exec
26
+ }
27
+ end
28
+ end
29
+ end