inprovise 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +28 -0
  4. data/Gemfile +9 -0
  5. data/LICENSE +8 -0
  6. data/README.md +197 -0
  7. data/Rakefile.rb +9 -0
  8. data/bin/rig +5 -0
  9. data/inprovise.gemspec +22 -0
  10. data/lib/inprovise/channel/ssh.rb +202 -0
  11. data/lib/inprovise/cli/group.rb +86 -0
  12. data/lib/inprovise/cli/node.rb +95 -0
  13. data/lib/inprovise/cli/provision.rb +84 -0
  14. data/lib/inprovise/cli.rb +105 -0
  15. data/lib/inprovise/cmd_channel.rb +100 -0
  16. data/lib/inprovise/cmd_helper.rb +150 -0
  17. data/lib/inprovise/control.rb +326 -0
  18. data/lib/inprovise/execution_context.rb +277 -0
  19. data/lib/inprovise/group.rb +67 -0
  20. data/lib/inprovise/helper/cygwin.rb +43 -0
  21. data/lib/inprovise/helper/linux.rb +181 -0
  22. data/lib/inprovise/helper/windows.rb +123 -0
  23. data/lib/inprovise/infra.rb +122 -0
  24. data/lib/inprovise/local_file.rb +120 -0
  25. data/lib/inprovise/logger.rb +79 -0
  26. data/lib/inprovise/node.rb +271 -0
  27. data/lib/inprovise/remote_file.rb +128 -0
  28. data/lib/inprovise/resolver.rb +36 -0
  29. data/lib/inprovise/script.rb +175 -0
  30. data/lib/inprovise/script_index.rb +46 -0
  31. data/lib/inprovise/script_runner.rb +110 -0
  32. data/lib/inprovise/sniff.rb +46 -0
  33. data/lib/inprovise/sniffer/linux.rb +64 -0
  34. data/lib/inprovise/sniffer/platform.rb +46 -0
  35. data/lib/inprovise/sniffer/unknown.rb +11 -0
  36. data/lib/inprovise/sniffer/windows.rb +32 -0
  37. data/lib/inprovise/template/inprovise.rb.erb +92 -0
  38. data/lib/inprovise/template.rb +38 -0
  39. data/lib/inprovise/trigger_runner.rb +36 -0
  40. data/lib/inprovise/version.rb +10 -0
  41. data/lib/inprovise.rb +145 -0
  42. data/test/cli_test.rb +314 -0
  43. data/test/cli_test_helper.rb +19 -0
  44. data/test/dsl_test.rb +43 -0
  45. data/test/fixtures/example.txt +1 -0
  46. data/test/fixtures/include.rb +4 -0
  47. data/test/fixtures/inprovise.rb +1 -0
  48. data/test/fixtures/myscheme.rb +1 -0
  49. data/test/infra_test.rb +189 -0
  50. data/test/local_file_test.rb +64 -0
  51. data/test/remote_file_test.rb +106 -0
  52. data/test/resolver_test.rb +66 -0
  53. data/test/script_index_test.rb +53 -0
  54. data/test/script_test.rb +56 -0
  55. data/test/test_helper.rb +237 -0
  56. metadata +182 -0
@@ -0,0 +1,110 @@
1
+ # Script runner for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ class Inprovise::ScriptRunner
7
+ COMMANDS = {apply: %w{Applying to}, revert: %w(Reverting on), validate: %w(Validating on)}
8
+
9
+ def initialize(node, script, skip_dependencies=false)
10
+ @node = node
11
+ @script = script
12
+ @index = Inprovise::ScriptIndex.default
13
+ @perform = true
14
+ @skip_dependencies = skip_dependencies
15
+ @log = Inprovise::Logger.new(@node, script)
16
+ end
17
+
18
+ def set_index(index)
19
+ @index = index
20
+ end
21
+
22
+ def script
23
+ Inprovise::Script === @script ? @script : @index.get(@script)
24
+ end
25
+
26
+ def scripts
27
+ return [script] if @skip_dependencies
28
+ resolver = Inprovise::Resolver.new(script, @index)
29
+ resolver.resolve
30
+ resolver.scripts
31
+ end
32
+
33
+ def execute(command_name, config=nil)
34
+ Inprovise.log.local("#{COMMANDS[command_name].first} #{script.name} #{COMMANDS[command_name].last} #{@node.to_s}")
35
+ scrs = scripts
36
+ scrs.reverse! if command_name.to_sym == :revert
37
+ @log.say scrs.map(&:name).join(', ').yellow if Inprovise.verbosity > 0
38
+ context = @perform ? Inprovise::ExecutionContext.new(@node, @log, @index, config) : Inprovise::MockExecutionContext.new(@node, @log, @index, config)
39
+ context.config.command = command_name
40
+ scrs.each { |script| script.merge_configuration(context.config) }
41
+ scrs.each do |script|
42
+ send(:"execute_#{command_name}", script, context)
43
+ end
44
+ end
45
+
46
+ def demonstrate(command_name, config=nil)
47
+ @perform = false
48
+ execute(command_name, config)
49
+ @perform = true
50
+ end
51
+
52
+ def execute_apply(script, context)
53
+ return unless should_run?(script, :apply, context)
54
+ exec(script, :apply, context)
55
+ validate!(script, context)
56
+ end
57
+
58
+ def execute_revert(script, context)
59
+ return unless should_run?(script, :revert, context)
60
+ exec(script, :revert, context)
61
+ end
62
+
63
+ def execute_validate(script, context)
64
+ validate!(script, context)
65
+ end
66
+
67
+ def should_run?(script, command_name, context)
68
+ return false unless script.provides_command?(command_name)
69
+ return true unless @perform
70
+ return true unless command_name == :apply || command_name == :revert
71
+ return true unless script.provides_command?(:validate)
72
+ is_present = is_valid?(script, context)
73
+ return !is_present if command_name == :apply
74
+ is_present
75
+ end
76
+
77
+ def validate!(script, context)
78
+ return true unless @perform
79
+ return unless script.provides_command?(:validate)
80
+ return if is_valid?(script, context)
81
+ raise ValidationFailureError.new(@node, script)
82
+ end
83
+
84
+ def is_valid?(script, context)
85
+ results = exec(script, :validate, context)
86
+ rc = results.all?
87
+ context.log.command("validate -> #{rc}") if Inprovise.verbosity > 0
88
+ rc
89
+ end
90
+
91
+ def exec(script, command_name, context)
92
+ cmds = script.command(command_name)
93
+ context = context.for_user(script.user) if script.user
94
+ context.log.set_task(script)
95
+ context.log.command(command_name)
96
+ context.script = script
97
+ cmds.map {|cmd| context.exec(cmd) }
98
+ end
99
+
100
+ class ValidationFailureError < StandardError
101
+ def initialize(node, script)
102
+ @node = node
103
+ @script = script
104
+ end
105
+
106
+ def message
107
+ "Script #{@script.name} failed validation on #{@node.to_s}"
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,46 @@
1
+ # Sniffer main module for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ module Inprovise::Sniffer
7
+
8
+ ROOT_SCRIPT = 'sniffers'
9
+
10
+ class << self
11
+
12
+ def sniffers
13
+ @sniffers ||= Inprovise::ScriptIndex.new('sniffers')
14
+ end
15
+
16
+ def add_sniffer(name, &definition)
17
+ Inprovise.log.local("Adding sniffer script #{name}") if Inprovise.verbosity > 2
18
+ script = Inprovise::Script.new(name)
19
+ Inprovise::Script::DSL.new(script).instance_eval(&definition) if block_given?
20
+ sniffers.add(script)
21
+ script
22
+ end
23
+ private :add_sniffer
24
+
25
+ def define(name, auto_trigger=true, &definition)
26
+ script = add_sniffer("sniff[#{name}]", &definition)
27
+ sniffers.get(ROOT_SCRIPT).triggers(script.name) if auto_trigger
28
+ script
29
+ end
30
+
31
+ def run_sniffers_for(node)
32
+ node.config[:attributes] ||= {}
33
+ runner = Inprovise::ScriptRunner.new(node, 'sniffers')
34
+ runner.set_index(@sniffers)
35
+ runner.execute(:apply)
36
+ end
37
+
38
+ end
39
+
40
+ # add root sniffer script
41
+ # (doesn't do anything by itself except provide a container triggering all specific sniffers)
42
+ add_sniffer(ROOT_SCRIPT)
43
+
44
+ end
45
+
46
+ require_relative './sniffer/platform.rb'
@@ -0,0 +1,64 @@
1
+ # Linux platform sniffer for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ Inprovise::Sniffer.define('linux', false) do
7
+
8
+ action('main') do |attrs|
9
+ attrs[:machine] = run('uname -m').chomp
10
+ if remote('/etc/os-release').exists?
11
+ trigger 'sniff[linux]:os-release', attrs
12
+ elsif remote('/etc/redhat-release').exists?
13
+ trigger 'sniff[linux]:redhat-release', attrs
14
+ elsif remote('/etc/SuSE-release').exists?
15
+ trigger 'sniff[linux]:suse-release', attrs
16
+ end
17
+ attrs[:pkgman] = case attrs[:os_distro]
18
+ when 'fedora', 'centos', 'rhel'
19
+ binary_exists?('dnf') ? 'dnf' : 'yum'
20
+ when /suse/
21
+ 'zypper'
22
+ end
23
+ end
24
+
25
+ action('os-release') do |attrs|
26
+ data = remote('/etc/os-release').content.split("\n").collect {|l| l.strip }
27
+ vars = data.inject({}) do |hash, line|
28
+ unless line.empty? || line.start_with?('#') || !(line =~ /[^=]+=.*/)
29
+ var, val = line.split('=')
30
+ hash[var] = val.strip.gsub(/(\A")|("\Z)/, '')
31
+ end
32
+ hash
33
+ end
34
+ attrs[:os_distro] = vars['ID'].downcase
35
+ attrs[:os_version] = vars['VERSION_ID']
36
+ if attrs[:os_distro] == 'centos' && remote('/etc/centos-release').exists?
37
+ data = remote('/etc/centos-release').content.split("\n").collect {|l| l.strip }
38
+ data.each do |line|
39
+ if line =~ /\s+release\s+(\d+)\.(\d+).*/
40
+ attrs[:os_version] = "#{$1}.#{$2}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ action('redhat-release') do |attrs|
47
+ data = remote('/etc/redhat-release').content.split("\n").collect {|l| l.strip }
48
+ data.each do |line|
49
+ if line =~ /\A(.+)\s+release\s+(\d+)(\.(\d+))?/
50
+ attrs[:os_version] = "#{$2}.#{$4 || '0'}"
51
+ tmpos = $1.strip.downcase
52
+ attrs[:os_distro] = case tmpos
53
+ when /fedora/
54
+ 'fedora'
55
+ when /red\s+hat/
56
+ 'rhel'
57
+ when /centos/
58
+ 'centos'
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,46 @@
1
+ # Platform sniffer for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ Inprovise::Sniffer.define('platform') do
7
+
8
+ action('helper') do |attrs|
9
+ # determin the best CmdHelper if not user defined
10
+ attrs[:helper] = case attrs[:os]
11
+ when 'linux'
12
+ 'linux'
13
+ when 'windows'
14
+ # check for Cygwin environment
15
+ ostype = node.channel.run('echo $OSTYPE').strip
16
+ # configure the Linux command helper here first;
17
+ # this way we can use the full context functionality from now on
18
+ node.config[:helper] = (/cygwin/i =~ ostype ? 'cygwin' : 'windows')
19
+ end unless attrs[:helper]
20
+ end
21
+
22
+ apply do
23
+ attrs = {}
24
+ os = node.channel.run('echo %OS%').chomp
25
+ os = node.channel.run('echo $OS').chomp if os == '%OS%'
26
+ os = node.channel.run('uname -o').chomp if os.empty?
27
+ attrs[:os] = case os
28
+ when /windows/i
29
+ 'windows'
30
+ when /linux/i
31
+ 'linux'
32
+ else
33
+ 'unknown'
34
+ end
35
+ # determin and initialize helper
36
+ trigger 'sniff[platform]:helper', attrs
37
+ # detect detailed platform props
38
+ trigger "sniff[#{attrs[:os]}]:main", attrs
39
+ (node.config[:attributes][:platform] ||= {}).merge!(attrs)
40
+ end
41
+
42
+ end
43
+
44
+ require_relative './windows.rb'
45
+ require_relative './linux.rb'
46
+ require_relative './unknown.rb'
@@ -0,0 +1,11 @@
1
+ # Unknown platform sniffer for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ Inprovise::Sniffer.define('unknown', false) do
7
+
8
+ action('main') do |attrs|
9
+ end
10
+
11
+ end
@@ -0,0 +1,32 @@
1
+ # Windows platform sniffer for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ Inprovise::Sniffer.define('windows', false) do
7
+
8
+ action('main') do |attrs|
9
+ attrs[:machine] = env('PROCESSOR_ARCHITECTURE').chomp =~ /amd64/i ? 'x86_64' : 'x86'
10
+ osver = run('cmd /c ver').strip
11
+ if /\[version\s+(\d+)\.(\d+)\.(\d+)\]/i =~ osver
12
+ attrs[:os_version] = case $1
13
+ when '5'
14
+ 'xp'
15
+ when '6'
16
+ case $2
17
+ when '1'
18
+ '7'
19
+ when '2'
20
+ '8'
21
+ when '3'
22
+ '8.1'
23
+ end
24
+ when '10'
25
+ '10'
26
+ else
27
+ $1
28
+ end
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,92 @@
1
+ # Scheme template for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ # include 'another_scheme.rb'
7
+
8
+ # script 'script' do
9
+ #
10
+ # description 'description'
11
+ #
12
+ # configuration <data>
13
+ #
14
+ # action('action1') { }
15
+ # action('action2') { }
16
+ #
17
+ # apply do
18
+ #
19
+ # end
20
+ #
21
+ # revert do
22
+ #
23
+ # end
24
+ #
25
+ # validate do
26
+ #
27
+ # end
28
+ # end
29
+ #
30
+ #
31
+ # All blocks (action,apply,revert,validate,file props,file block) execute in a special context providing the
32
+ # following methods:
33
+ #
34
+ # node current node
35
+ #
36
+ # config current (consolidated, transient) config
37
+ #
38
+ # as 'user' do ... end creates a nested execution context where all remote commands
39
+ # will be executed for the given user
40
+ #
41
+ # in_dir 'path' do ... end creates a nested execution context where all remote commands
42
+ # will be executed using the given path as working directory
43
+ #
44
+ # trigger 'script:action'[,*args] runs specified action code blocks
45
+ #
46
+ # run 'command'[, {}] execute command on current node over SSH connection
47
+ # options:
48
+ # :once => true
49
+ # run command only once (from whatever script executes it first)
50
+ # :log => true
51
+ # force logging SSH commands and output
52
+ #
53
+ # run_local 'command' execute command locally
54
+ #
55
+ # log 'msg' log a message
56
+ #
57
+ # sudo 'command'[, {}] execute command on current node over SSH connection using 'sudo'
58
+ # options: see 'run'
59
+ #
60
+ # env 'var' returns value of environment variable on current node
61
+ #
62
+ # upload '/from/local/path', '/to/remote/path' up-/download files using SFTP connection for current node
63
+ # download '/from/remote/path', '/to/local/path'
64
+ #
65
+ # local('/local/path') creates a local/remote file object providing the following
66
+ # remote('/remote/path') methods:
67
+ # .hash returns an SHA1 hash as hex string
68
+ # .exists?
69
+ # .file?
70
+ # .directory?
71
+ # .content
72
+ # .matches?(other_file)
73
+ # .copy_to(dest_file)
74
+ # .copy_from(src_file)
75
+ # .delete!
76
+ # .permissions
77
+ # .set_permissions
78
+ # .user
79
+ # .group
80
+ # .set_owner('user'[,'group'])
81
+ # .is_local?
82
+ #
83
+ # mkdir '/remote/path' create directory(-ies) on current node
84
+ #
85
+ # binary_exists?('bin-name')
86
+ #
87
+ # template('/local/file') creates a template object for the specified (ERB) template file
88
+ # providing 2 rendering options
89
+ # .render(locals ={}) returns rendered result string
90
+ # .render_to_tempfile(locals = {}) returns path of tempfile containing rendered result
91
+ #
92
+
@@ -0,0 +1,38 @@
1
+ # Template support for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ require 'erb'
7
+ require 'tilt'
8
+ require 'tempfile'
9
+
10
+ class Inprovise::Template
11
+ def initialize(path, context = nil)
12
+ @context = context || Object.new
13
+ @path = resolve(path)
14
+ @template = @path.respond_to?(:call) ? Tilt['erb'].new(&@path) : Tilt.new(@path)
15
+ end
16
+
17
+ def render(locals={})
18
+ @template.render(@context, locals)
19
+ end
20
+
21
+ def render_to_tempfile(locals={})
22
+ basename = @path.respond_to?(:call) ? 'inprovise-inline-tpl' : File.basename(@path).gsub('.', '-')
23
+ file = Tempfile.new(basename)
24
+ file.write render(locals)
25
+ file.close
26
+ file.path
27
+ end
28
+
29
+ private
30
+
31
+ def resolve(path)
32
+ if path.respond_to?(:call) || path =~ /^\//
33
+ path
34
+ else
35
+ File.join(Inprovise.root, path)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ # Trigger runner for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ class Inprovise::TriggerRunner
7
+ def initialize(node, action_ref_with_args)
8
+ @node = node
9
+ @action_ref, @args = *parse_action_ref(action_ref_with_args)
10
+ @log = Inprovise::Logger.new(@node, @action_ref)
11
+ @index = Inprovise::ScriptIndex.default
12
+ end
13
+
14
+ def set_index(index)
15
+ @index = index
16
+ end
17
+
18
+ def execute(_, config=nil)
19
+ Inprovise.log.local("Triggering #{@action_ref} for #{@node.to_s}")
20
+ Inprovise::ExecutionContext.new(@node, @log, @index, config).trigger(@action_ref, *@args)
21
+ end
22
+
23
+ def demonstrate(_, config=nil)
24
+ Inprovise::MockExecutionContext.new(@node, @log, @index, config).trigger(@action_ref, *@args)
25
+ end
26
+
27
+ private
28
+
29
+ def parse_action_ref(action_ref_with_args)
30
+ matches = action_ref_with_args.match(/([\w\-\:]+?)(\[([\w\-\,]+?)\])/)
31
+ return [action_ref_with_args,[]] unless matches
32
+ action_ref = matches[1]
33
+ args = matches[3].split(',').map(&:strip)
34
+ [action_ref, args]
35
+ end
36
+ end
@@ -0,0 +1,10 @@
1
+ # Version definition for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ module Inprovise
7
+
8
+ VERSION = '0.2.2'
9
+
10
+ end
data/lib/inprovise.rb ADDED
@@ -0,0 +1,145 @@
1
+ # Main loader for Inprovise
2
+ #
3
+ # Author:: Martin Corino
4
+ # License:: Distributes under the same license as Ruby
5
+
6
+ require 'rubygems'
7
+ require 'colored'
8
+
9
+ module Inprovise
10
+
11
+ INFRA_FILE = 'infra.json'
12
+ RC_FILE = 'rigrc'
13
+ DEFAULT_SCHEME = 'inprovise.rb'
14
+
15
+ class << self
16
+ def verbosity
17
+ @verbose ||= 0
18
+ end
19
+
20
+ def verbosity=(val)
21
+ @verbose = val.to_i
22
+ end
23
+
24
+ def show_backtrace
25
+ @show_backtrace ||= false
26
+ end
27
+
28
+ def show_backtrace=(f)
29
+ @show_backtrace = (f == true)
30
+ end
31
+
32
+ def sequential
33
+ @sequential ||= false
34
+ end
35
+
36
+ def sequential=(f)
37
+ @sequential = (f == true)
38
+ end
39
+
40
+ def demonstrate
41
+ @demonstrate ||= false
42
+ end
43
+
44
+ def demonstrate=(f)
45
+ @demonstrate = (f == true)
46
+ end
47
+
48
+ def skip_dependencies
49
+ @skip_dependencies ||= false
50
+ end
51
+
52
+ def skip_dependencies=(f)
53
+ @skip_dependencies = (f == true)
54
+ end
55
+
56
+ def infra
57
+ @infra ||= (ENV['INPROVISE_INFRA'] || find_infra)
58
+ end
59
+
60
+ def root
61
+ @root ||= File.dirname(infra)
62
+ end
63
+
64
+ def default_scheme
65
+ ENV['INPROVISE_SCHEME'] || Inprovise::DEFAULT_SCHEME
66
+ end
67
+
68
+ def schemes
69
+ @schemes ||= []
70
+ end
71
+
72
+ def loaded?(scheme)
73
+ schemes.include?(File.expand_path(scheme, root))
74
+ end
75
+
76
+ def log
77
+ @log ||= Inprovise::Logger.new('Local', 'cli')
78
+ end
79
+
80
+ def add_script(script)
81
+ yield(script) if block_given?
82
+ Inprovise::ScriptIndex.default.add(script)
83
+ script
84
+ end
85
+
86
+ private
87
+
88
+ def find_infra
89
+ curpath = File.expand_path('.')
90
+ begin
91
+ # check if this is where the infra file lives
92
+ if File.file?(File.join(curpath, Inprovise::INFRA_FILE))
93
+ return File.join(curpath, Inprovise::INFRA_FILE)
94
+ end
95
+ # not found yet, move one dir up until we reach the root
96
+ curpath = File.expand_path(File.join(curpath, '..'))
97
+ end while !(curpath =~ /^(#{File::SEPARATOR}|.:#{File::SEPARATOR})$/)
98
+ INFRA_FILE
99
+ end
100
+ end
101
+
102
+ module DSL
103
+
104
+ def self.singleton_class
105
+ class << self; self; end
106
+ end unless self.respond_to?(:singleton_class)
107
+
108
+ singleton_class.class_eval do
109
+ def dsl_define(*args, &block)
110
+ Inprovise::DSL.singleton_class.class_eval(*args, &block)
111
+ end
112
+ end
113
+
114
+ dsl_define do
115
+ def include(path)
116
+ path = File.expand_path(path, Inprovise.root)
117
+ unless Inprovise.schemes.include?(path)
118
+ Inprovise.schemes << path
119
+ Inprovise.log.local("Loading provisioning scheme #{path}") if Inprovise.verbosity > 0
120
+ Inprovise::DSL.module_eval(File.read(path), path)
121
+ end
122
+ end
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+
129
+ require_relative './inprovise/version'
130
+ require_relative './inprovise/logger'
131
+ require_relative './inprovise/cmd_channel'
132
+ require_relative './inprovise/cmd_helper'
133
+ require_relative './inprovise/script'
134
+ require_relative './inprovise/script_index'
135
+ require_relative './inprovise/local_file'
136
+ require_relative './inprovise/remote_file'
137
+ require_relative './inprovise/script_runner'
138
+ require_relative './inprovise/trigger_runner'
139
+ require_relative './inprovise/resolver'
140
+ require_relative './inprovise/template'
141
+ require_relative './inprovise/execution_context'
142
+ require_relative './inprovise/infra'
143
+ require_relative './inprovise/sniff'
144
+ require_relative './inprovise/control'
145
+ require_relative './inprovise/cli'