yle_tf 0.1.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e7b1c7b056ec918749df48648c4648f2c48f1ea1
4
- data.tar.gz: a914bf80887e137d875a462ff6fa41e0d90719e7
3
+ metadata.gz: dc3f6bcfcb30e2863c067ca306b13a834ec66444
4
+ data.tar.gz: 3ba84a88565e830b0686f49532259fab3c466d82
5
5
  SHA512:
6
- metadata.gz: '097b3721ffacb30739b37cd9eacfa583a582f8e210778869a47d5f47c18269f98df41b68387e996199abd879855d9bddcf9100d2dd278793cb837ac9abdcbc83'
7
- data.tar.gz: 353f0124461ff8cfd6c742f0a4eeae635793c9dabc39cce25b3aac64b76325aedfb1d62118b831d79a73b98cd15696dc4be7b659812b48fc06e0ab8999f05725
6
+ metadata.gz: d438544dfde60a06feb23f9c12ba19230c6892fdaeaa43bcf7aa3360f1d34469b65ec13fd8184b920ba6ec76602644e2f1583678f469bbac66f5116b13302f68
7
+ data.tar.gz: 7a136fb090de4474ffd55e664d371374cd70dcab53dd6851f2c85ed6ce89364b5eb105e42c6131c5863d0894c7c990bc974566a227da31d92d6b5ff6159a66c5
@@ -15,8 +15,8 @@ class YleTf
15
15
  config = env[:config]
16
16
  backend = backend_config(config)
17
17
 
18
- Logger.debug('Initializing Terraform with backend configuration:')
19
- Logger.debug(backend.to_s)
18
+ Logger.info('Initializing Terraform')
19
+ Logger.debug("Backend configuration: #{backend}")
20
20
 
21
21
  if VersionRequirement.pre_0_9?(env[:terraform_version])
22
22
  init_pre_0_9(backend)
@@ -29,17 +29,21 @@ class YleTf
29
29
 
30
30
  def init_pre_0_9(backend)
31
31
  cli_args = backend.cli_args
32
- YleTf::System.cmd('terraform', 'remote', 'config', *cli_args) if cli_args
32
+ if cli_args
33
+ YleTf::System.cmd('terraform', 'remote', 'config',
34
+ '-no-color', *cli_args,
35
+ stdout: :debug)
36
+ end
33
37
 
34
38
  Logger.debug('Fetching Terraform modules')
35
- YleTf::System.cmd('terraform', 'get')
39
+ YleTf::System.cmd('terraform', 'get', '-no-color', stdout: :debug)
36
40
  end
37
41
 
38
42
  def init(backend)
39
43
  Logger.debug('Generating the backend configuration')
40
44
  backend.generate_config do
41
45
  Logger.debug('Initializing Terraform')
42
- YleTf::System.cmd('terraform', 'init', '-no-color')
46
+ YleTf::System.cmd('terraform', 'init', '-no-color', stdout: :debug)
43
47
  end
44
48
  end
45
49
 
@@ -20,7 +20,7 @@ class YleTf
20
20
  @app.call(env)
21
21
  end
22
22
  ensure
23
- FileUtils.rm_r(tmpdir) if tmpdir
23
+ FileUtils.rm_r(tmpdir) if tmpdir && Dir.exist?(tmpdir)
24
24
  end
25
25
 
26
26
  def tmpdir_prefix(config)
@@ -1,5 +1,6 @@
1
1
  require 'yle_tf/error'
2
2
  require 'yle_tf/logger'
3
+ require 'yle_tf/system'
3
4
  require 'yle_tf/version_requirement'
4
5
 
5
6
  class YleTf
@@ -22,10 +23,8 @@ class YleTf
22
23
  end
23
24
 
24
25
  def terraform_version
25
- # TODO: move `command` to YleTf::System
26
- Regexp.last_match(1) if `terraform version` =~ /^Terraform v([^\s]+)/
27
- rescue Errno::ENOENT
28
- nil
26
+ v = YleTf::System.read_cmd('terraform', 'version', error_handler: proc {})
27
+ Regexp.last_match(1) if v =~ /^Terraform v([^\s]+)/
29
28
  end
30
29
 
31
30
  def verify_version(env)
@@ -12,9 +12,13 @@ class YleTf
12
12
  config = env[:config]
13
13
  all_envs = VarsFile.list_all_envs(config)
14
14
 
15
+ if all_envs.empty?
16
+ raise Error, "No Terraform vars files found in '#{VarsFile::ENV_DIR}/'"
17
+ end
18
+
15
19
  if !all_envs.include?(config.tf_env)
16
20
  raise Error, "Terraform vars file not found for the '#{config.tf_env}' " \
17
- " environment. Existing envs: #{all_envs.join(', ')}"
21
+ "environment. Existing envs: #{all_envs.join(', ')}"
18
22
  end
19
23
 
20
24
  @app.call(env)
data/lib/yle_tf/cli.rb CHANGED
@@ -5,7 +5,7 @@ class YleTf
5
5
  attr_reader :tf_options, :tf_command, :tf_command_args, :tf_env
6
6
 
7
7
  # YleTf option arguments
8
- TF_OPTIONS = %w[--debug --no-hooks --only-hooks].freeze
8
+ TF_OPTIONS = %w[--debug --no-color --no-hooks --only-hooks].freeze
9
9
 
10
10
  HELP_ARGS = %w[-h --help help].freeze
11
11
  VERSION_ARGS = %w[-v --version version].freeze
@@ -65,7 +65,8 @@ class YleTf
65
65
  @tf_env = 'error'
66
66
  end
67
67
 
68
- self.debug = true if @tf_options.include?(:debug)
68
+ self.debug = true if @tf_options[:debug]
69
+ YleTf::Logger.color = !@tf_options[:no_color]
69
70
  end
70
71
 
71
72
  # Returns `Symbol` for the arg, e.g. `"--foo-bar"` -> `:foo_bar`
@@ -0,0 +1,22 @@
1
+ class YleTf
2
+ module Logger
3
+ module Colorize
4
+ COLORS = {
5
+ black: 30,
6
+ red: 31,
7
+ brown: 33,
8
+ blue: 34,
9
+ magenta: 35,
10
+ cyan: 36,
11
+ gray: 37
12
+ }.freeze
13
+
14
+ # Wraps the message with the specified ANSI color code
15
+ # if the color is specified and supported
16
+ def self.colorize(msg, color)
17
+ code = COLORS[color]
18
+ code ? "\033[#{code}m#{msg}\033[0m" : msg
19
+ end
20
+ end
21
+ end
22
+ end
data/lib/yle_tf/logger.rb CHANGED
@@ -1,14 +1,17 @@
1
1
  require 'forwardable'
2
2
  require 'logger'
3
3
  require 'rubygems'
4
+ require 'yle_tf/logger/colorize'
4
5
 
5
6
  class YleTf
6
7
  # Logger for debug, error, etc. outputs.
7
8
  # Prints to STDERR, so it does not mess with e.g. `terraform output`.
8
9
  module Logger
10
+ LEVELS = %i[debug info warn error fatal].freeze
11
+
9
12
  class << self
10
13
  extend Forwardable
11
- def_delegators :logger, :debug, :info, :warn, :error, :fatal
14
+ def_delegators :logger, *LEVELS
12
15
  def_delegators :logger, :debug?
13
16
  end
14
17
 
@@ -28,14 +31,35 @@ class YleTf
28
31
 
29
32
  def self.log_formatter
30
33
  proc do |severity, _datetime, progname, msg|
34
+ msg = Colorize.colorize(msg, color(severity))
35
+
31
36
  if progname
32
- "[#{progname}] #{severity}: #{msg}\n"
37
+ "#{severity}: [#{progname}] #{msg}\n"
33
38
  else
34
39
  "#{severity}: #{msg}\n"
35
40
  end
36
41
  end
37
42
  end
38
43
 
44
+ def self.color?
45
+ @color
46
+ end
47
+
48
+ def self.color=(value)
49
+ @color = value
50
+ end
51
+
52
+ def self.color(severity)
53
+ return if !color?
54
+
55
+ case severity.to_s
56
+ when 'FATAL', 'ERROR'
57
+ :red
58
+ when 'WARN'
59
+ :brown
60
+ end
61
+ end
62
+
39
63
  # Patches the `::Logger` in older Ruby versions to
40
64
  # accept log level as a `String`
41
65
  def self.patch_for_old_ruby(logger)
@@ -0,0 +1,105 @@
1
+ require 'yle_tf/system/output_logger'
2
+
3
+ class YleTf
4
+ class System
5
+ class IOHandlers
6
+ BLOCK_SIZE = 1024
7
+
8
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
9
+ def self.input_handler(handler)
10
+ case handler
11
+ when :close
12
+ close
13
+ when :console
14
+ console_input
15
+ when :dev_null
16
+ dev_null_input
17
+ when IO, StringIO
18
+ io_input(handler)
19
+ else
20
+ if !handler.respond_to?(:call)
21
+ raise YleTf::Error, "Unknown input handler #{handler.inspect}"
22
+ end
23
+ handler
24
+ end
25
+ end
26
+
27
+ def self.output_handler(handler)
28
+ case handler
29
+ when :close
30
+ close
31
+ when :console
32
+ console_output
33
+ when :dev_null
34
+ dev_null_output
35
+ when IO, StringIO
36
+ io_output(handler)
37
+ when Symbol
38
+ OutputLogger.new(handler)
39
+ else
40
+ if !handler.respond_to?(:call)
41
+ raise YleTf::Error, "Unknown output handler #{handler.inspect}"
42
+ end
43
+ handler
44
+ end
45
+ end
46
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
47
+
48
+ # Returns a lambda that just closes the IO
49
+ def self.close
50
+ ->(io, *) { io.close }
51
+ end
52
+
53
+ # Returns a lambda that pipes STDIN to the IO
54
+ def self.console_input
55
+ io_input(STDIN)
56
+ end
57
+
58
+ # Returns a lambda that pipes IO's output to STDOUT
59
+ def self.console_output
60
+ io_output(STDOUT)
61
+ end
62
+
63
+ # Returns a lambda that does nothing
64
+ def self.dev_null_input
65
+ ->(*) {}
66
+ end
67
+
68
+ # Returns a lambda that just consumes the IO's output
69
+ def self.dev_null_output
70
+ lambda do |io, *|
71
+ Thread.new do
72
+ while io.read; end
73
+ end
74
+ end
75
+ end
76
+
77
+ # Returns a lambda that pipes the source IO to the IO's input
78
+ def self.io_input(source)
79
+ lambda do |target, *|
80
+ Thread.new do
81
+ begin
82
+ while (data = source.readpartial(BLOCK_SIZE))
83
+ target.write(data)
84
+ end
85
+ ensure
86
+ target.close_write
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ # Returns a lambda that pipes IO's output to the target IO
93
+ # Does not close the target stream
94
+ def self.io_output(target)
95
+ lambda do |source, *|
96
+ Thread.new do
97
+ while (data = source.readpartial(BLOCK_SIZE))
98
+ target.write(data)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,29 @@
1
+ require 'yle_tf/logger'
2
+
3
+ class YleTf
4
+ class System
5
+ class OutputLogger
6
+ attr_reader :level
7
+
8
+ def initialize(level)
9
+ @level = level.to_sym
10
+
11
+ raise YleTf::Error, "Unknown log level '#{@level}'" if !level?(@level)
12
+ end
13
+
14
+ def call(io, progname)
15
+ Thread.new do
16
+ io.each { |line| log(progname, line.chomp) }
17
+ end
18
+ end
19
+
20
+ def level?(level)
21
+ YleTf::Logger::LEVELS.include?(level)
22
+ end
23
+
24
+ def log(progname, line)
25
+ YleTf::Logger.public_send(level, progname) { line }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ require 'yle_tf/logger'
2
+ require 'yle_tf/system/output_logger'
3
+
4
+ class YleTf
5
+ class System
6
+ # An IO handler class for YleTf Hook output.
7
+ #
8
+ # Allows hooks to emit log messages with specific levels
9
+ # by prefixing a line with `<LEVEL>: `.
10
+ class TfHookOutputLogger < OutputLogger
11
+ def log(progname, line)
12
+ # Remove `[<progname>] ` prefix from the output line.
13
+ # This is mostly for backwards compatibility in Yle.
14
+ line.sub!(/^\[#{progname}\] /, '')
15
+
16
+ level, line = line_level(line)
17
+
18
+ YleTf::Logger.public_send(level, progname) { line }
19
+ end
20
+
21
+ # Extracts the log level from the line if found,
22
+ # otherwise returns the default level and the line as is
23
+ def line_level(line)
24
+ if (m = /^(?<level>[A-Z]+): (?<line>.*)$/.match(line))
25
+ line_level = m[:level].downcase.to_sym
26
+ return [line_level, m[:line]] if level?(line_level)
27
+ end
28
+
29
+ [level, line]
30
+ end
31
+ end
32
+ end
33
+ end
data/lib/yle_tf/system.rb CHANGED
@@ -1,22 +1,98 @@
1
+ require 'English'
2
+ require 'open3'
1
3
  require 'shellwords'
2
-
4
+ require 'thwait'
3
5
  require 'yle_tf/error'
4
6
  require 'yle_tf/logger'
7
+ require 'yle_tf/system/io_handlers'
5
8
 
6
9
  class YleTf
7
10
  # Helpers to execute system commands with error handling
8
- #
9
- # TODO: Add way to wrap stdout of the commands and direct it to `Logger`
10
11
  class System
11
12
  ExecuteError = Class.new(YleTf::Error)
12
13
 
14
+ DEFAULT_ERROR_HANDLER = ->(_exit_code, error) { raise error }.freeze
15
+
16
+ DEFAULT_IO_HANDLERS = {
17
+ stdin: :dev_null,
18
+ stdout: :info,
19
+ stderr: :error
20
+ }.freeze
21
+
22
+ def self.console_cmd(*args, **opts)
23
+ env = opts[:env] || {}
24
+
25
+ YleTf::Logger.debug { "Calling #{cmd_string(args, env)}" }
26
+
27
+ system(env, *args)
28
+ verify_exit_status($CHILD_STATUS, opts[:error_handler], cmd_string(args))
29
+ end
30
+
31
+ # Executes the command and attaches IO streams
13
32
  def self.cmd(*args, **opts)
14
- env = opts[:env]
15
- YleTf::Logger.debug { "Calling `#{args.shelljoin}`#{" with env '#{env}'" if env}" }
33
+ opts = DEFAULT_IO_HANDLERS.merge(opts)
34
+ env = opts[:env] || {}
35
+ progname = opts.fetch(:progname) { args.first }
36
+
37
+ YleTf::Logger.debug { "Calling #{cmd_string(args, env)}" }
38
+
39
+ status = Open3.popen3(env, *args, &handle_io(progname, opts))
40
+ verify_exit_status(status, opts[:error_handler], cmd_string(args))
41
+ rescue Interrupt, Errno::ENOENT => e
42
+ error(opts[:error_handler], "Failed to execute #{cmd_string(args)}: #{e}")
43
+ end
44
+
45
+ def self.read_cmd(*args, **opts)
46
+ buffer = StringIO.new
47
+ cmd(*args, opts.merge(stdout: buffer))
48
+ buffer.string
49
+ end
50
+
51
+ def self.cmd_string(args, env = nil)
52
+ "`#{args.shelljoin}`#{" with env '#{env}'" if env && !env.empty?}"
53
+ end
54
+
55
+ def self.handle_io(progname, handlers)
56
+ lambda do |stdin, stdout, stderr, wait_thr|
57
+ in_thr = attach_input_handler(handlers[:stdin], stdin, progname)
58
+ out_thr = [
59
+ attach_output_handler(handlers[:stdout], stdout, progname),
60
+ attach_output_handler(handlers[:stderr], stderr, progname)
61
+ ]
62
+
63
+ # Wait for the process to exit
64
+ wait_thr.value.tap do
65
+ YleTf::Logger.debug("`#{progname}` exited, killing input handler thread")
66
+ in_thr.kill if in_thr.is_a?(Thread)
67
+
68
+ YleTf::Logger.debug('Waiting for output handler threads to stop')
69
+ ThreadsWait.all_waits(out_thr)
70
+ end
71
+ end
72
+ end
73
+
74
+ def self.attach_input_handler(handler, io, progname)
75
+ io_proc = IOHandlers.input_handler(handler)
76
+ io_proc.call(io, progname)
77
+ end
78
+
79
+ def self.attach_output_handler(handler, io, progname)
80
+ io_proc = IOHandlers.output_handler(handler)
81
+ io_proc.call(io, progname)
82
+ end
83
+
84
+ def self.verify_exit_status(status, handler, cmd)
85
+ status.success? ||
86
+ error(handler,
87
+ "Failed to execute #{cmd} (#{status.exitstatus})",
88
+ status.exitstatus)
89
+ end
90
+
91
+ def self.error(handler, error_msg, exit_code = nil)
92
+ YleTf::Logger.debug(error_msg)
16
93
 
17
- system(env || {}, *args) ||
18
- raise(ExecuteError,
19
- "Failed to execute `#{args.shelljoin}`#{" with env '#{env}'" if env}")
94
+ handler ||= DEFAULT_ERROR_HANDLER
95
+ handler.call(exit_code, ExecuteError.new(error_msg))
20
96
  end
21
97
  end
22
98
  end
@@ -4,6 +4,7 @@ require 'tmpdir'
4
4
  require 'yle_tf/error'
5
5
  require 'yle_tf/logger'
6
6
  require 'yle_tf/system'
7
+ require 'yle_tf/system/tf_hook_output_logger'
7
8
 
8
9
  class YleTf
9
10
  class TfHook
@@ -39,8 +40,14 @@ class YleTf
39
40
  def run(tf_vars)
40
41
  fetch if !path
41
42
 
42
- Logger.info("Running hook '#{description}'...")
43
- YleTf::System.cmd(path, env: vars.merge(tf_vars))
43
+ Logger.info("Running hook '#{description}'")
44
+ YleTf::System.cmd(
45
+ path,
46
+ env: vars.merge(tf_vars),
47
+ progname: File.basename(path),
48
+ stdout: System::TfHookOutputLogger.new(:info),
49
+ stderr: System::TfHookOutputLogger.new(:error)
50
+ )
44
51
  ensure
45
52
  delete_tmpdir
46
53
  end
@@ -64,9 +71,9 @@ class YleTf
64
71
  end
65
72
 
66
73
  def clone_git_repo(config)
67
- Logger.info("Cloning hook '#{description}' from #{config[:uri]} (#{config[:ref]})")
74
+ Logger.debug("Cloning hook '#{description}' from #{config[:uri]} (#{config[:ref]})")
68
75
  YleTf::System.cmd(
69
- 'git', 'clone', '--no-progress', '--depth=1', '--branch', config[:ref],
76
+ 'git', 'clone', '--quiet', '--depth=1', '--branch', config[:ref],
70
77
  '--', config[:uri], config[:dir]
71
78
  )
72
79
  end
@@ -76,7 +83,7 @@ class YleTf
76
83
  end
77
84
 
78
85
  def delete_tmpdir
79
- FileUtils.rm_r(@tmpdir) if @tmpdir
86
+ FileUtils.rm_r(@tmpdir) if @tmpdir && Dir.exist?(@tmpdir)
80
87
  @tmpdir = nil
81
88
  end
82
89
 
@@ -1,14 +1,16 @@
1
1
  class YleTf
2
2
  class VarsFile
3
+ ENV_DIR = 'envs'.freeze
4
+
3
5
  # Returns the env specific tfvars file path if it exists
4
6
  def self.find_env_vars_file(config)
5
- path = "#{config.module_dir}/envs/#{config.tf_env}.tfvars"
7
+ path = "#{config.module_dir}/#{ENV_DIR}/#{config.tf_env}.tfvars"
6
8
  VarsFile.new(path) if File.exist?(path)
7
9
  end
8
10
 
9
11
  # Returns all envs that have tfvars files
10
12
  def self.list_all_envs(config)
11
- Dir.glob("#{config.module_dir}/envs/*.tfvars").map do |path|
13
+ Dir.glob("#{config.module_dir}/#{ENV_DIR}/*.tfvars").map do |path|
12
14
  File.basename(path, '.tfvars')
13
15
  end
14
16
  end
@@ -1,3 +1,3 @@
1
1
  class YleTf
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -1,3 +1,4 @@
1
+ require 'yle_tf/logger'
1
2
  require 'yle_tf/system'
2
3
 
3
4
  module YleTfPlugins
@@ -5,9 +6,16 @@ module YleTfPlugins
5
6
  class Command
6
7
  def execute(env)
7
8
  command = env[:tf_command]
8
- args = env[:tf_command_args]
9
9
 
10
- YleTf::System.cmd('terraform', command, *args)
10
+ args = env[:tf_command_args].dup
11
+ args << '-no-color' if !color?(env)
12
+
13
+ YleTf::Logger.info "Running `terraform #{command}`"
14
+ YleTf::System.console_cmd('terraform', command, *args)
15
+ end
16
+
17
+ def color?(env)
18
+ !env[:tf_options][:no_color]
11
19
  end
12
20
  end
13
21
  end
@@ -1,5 +1,6 @@
1
1
  require 'optparse'
2
2
 
3
+ require 'yle_tf/system'
3
4
  require 'yle_tf/plugin'
4
5
 
5
6
  module YleTfPlugins
@@ -20,6 +21,7 @@ module YleTfPlugins
20
21
  o.on('-h', '--help', 'Prints this help')
21
22
  o.on('-v', '--version', 'Prints the version information')
22
23
  o.on('--debug', 'Print debug information')
24
+ o.on('--no-color', 'Do not output with colors')
23
25
  o.on('--no-hooks', 'Do not run any hooks')
24
26
  o.on('--only-hooks', 'Only run the hooks')
25
27
  o.separator ''
@@ -46,9 +48,14 @@ module YleTfPlugins
46
48
  end
47
49
 
48
50
  def terraform_help
49
- `terraform help`.lines.grep(/^ /)
50
- rescue Errno::ENOENT
51
- ' [Terraform not found]'
51
+ on_error = proc do |exit_code|
52
+ # exit_code is nil if the command was not found
53
+ # Ignore other exit codes, as older Terraform versions return
54
+ # non-zero exit codes for the help.
55
+ return ' [Terraform not found]' if exit_code.nil?
56
+ end
57
+ help = YleTf::System.read_cmd('terraform', '--help', error_handler: on_error)
58
+ help.lines.grep(/^ /)
52
59
  end
53
60
  end
54
61
  end
@@ -1,5 +1,4 @@
1
- require 'optparse'
2
-
1
+ require 'yle_tf/system'
3
2
  require 'yle_tf/version'
4
3
 
5
4
  module YleTfPlugins
@@ -11,9 +10,9 @@ module YleTfPlugins
11
10
  end
12
11
 
13
12
  def terraform_version
14
- `terraform version`.lines.first
15
- rescue Errno::ENOENT
16
- '[Terraform not found]'
13
+ on_error = proc { return '[Terraform not found]' }
14
+ v = YleTf::System.read_cmd('terraform', 'version', error_handler: on_error)
15
+ v.lines.first
17
16
  end
18
17
  end
19
18
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yle_tf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yleisradio
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-08-31 00:00:00.000000000 Z
13
+ date: 2017-09-18 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -86,11 +86,15 @@ files:
86
86
  - lib/yle_tf/config/loader.rb
87
87
  - lib/yle_tf/error.rb
88
88
  - lib/yle_tf/logger.rb
89
+ - lib/yle_tf/logger/colorize.rb
89
90
  - lib/yle_tf/plugin.rb
90
91
  - lib/yle_tf/plugin/action_hook.rb
91
92
  - lib/yle_tf/plugin/loader.rb
92
93
  - lib/yle_tf/plugin/manager.rb
93
94
  - lib/yle_tf/system.rb
95
+ - lib/yle_tf/system/io_handlers.rb
96
+ - lib/yle_tf/system/output_logger.rb
97
+ - lib/yle_tf/system/tf_hook_output_logger.rb
94
98
  - lib/yle_tf/tf_hook.rb
95
99
  - lib/yle_tf/tf_hook/runner.rb
96
100
  - lib/yle_tf/vars_file.rb