careacademy-runbook 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +17 -0
  3. data/.github/workflows/ruby.yml +43 -0
  4. data/.gitignore +16 -0
  5. data/.rspec +2 -0
  6. data/.ruby-gemset +1 -0
  7. data/.ruby-version +1 -0
  8. data/Appraisals +8 -0
  9. data/CHANGELOG.md +143 -0
  10. data/CODE_OF_CONDUCT.md +74 -0
  11. data/Gemfile +6 -0
  12. data/LICENSE.txt +21 -0
  13. data/README.md +1272 -0
  14. data/Rakefile +12 -0
  15. data/TODO.md +316 -0
  16. data/bin/console +16 -0
  17. data/bin/setup +8 -0
  18. data/dockerfiles/Dockerfile-runbook +18 -0
  19. data/dockerfiles/Dockerfile-sshd +4 -0
  20. data/examples/hooks_runbook.rb +72 -0
  21. data/examples/layout_runbook.rb +26 -0
  22. data/examples/restart_nginx.rb +26 -0
  23. data/examples/simple_runbook.rb +41 -0
  24. data/examples/suppress_capture_output.rb +47 -0
  25. data/exe/runbook +5 -0
  26. data/gemfiles/.bundle/config +2 -0
  27. data/gemfiles/activesupport_5.gemfile +7 -0
  28. data/gemfiles/activesupport_6.gemfile +7 -0
  29. data/images/runbook_anatomy_diagram.png +0 -0
  30. data/images/runbook_example.gif +0 -0
  31. data/images/runbook_execution_modes.png +0 -0
  32. data/lib/hacks/ssh_kit.rb +58 -0
  33. data/lib/runbook/airbrussh_context.rb +25 -0
  34. data/lib/runbook/cli.rb +115 -0
  35. data/lib/runbook/cli_base.rb +38 -0
  36. data/lib/runbook/configuration.rb +120 -0
  37. data/lib/runbook/dsl.rb +21 -0
  38. data/lib/runbook/entities/book.rb +17 -0
  39. data/lib/runbook/entities/section.rb +7 -0
  40. data/lib/runbook/entities/setup.rb +7 -0
  41. data/lib/runbook/entities/step.rb +7 -0
  42. data/lib/runbook/entity.rb +129 -0
  43. data/lib/runbook/errors.rb +7 -0
  44. data/lib/runbook/extensions/add.rb +14 -0
  45. data/lib/runbook/extensions/description.rb +14 -0
  46. data/lib/runbook/extensions/sections.rb +19 -0
  47. data/lib/runbook/extensions/setup.rb +17 -0
  48. data/lib/runbook/extensions/shared_variables.rb +51 -0
  49. data/lib/runbook/extensions/ssh_config.rb +78 -0
  50. data/lib/runbook/extensions/statements.rb +31 -0
  51. data/lib/runbook/extensions/steps.rb +24 -0
  52. data/lib/runbook/extensions/tmux.rb +13 -0
  53. data/lib/runbook/generator.rb +38 -0
  54. data/lib/runbook/generators/base.rb +45 -0
  55. data/lib/runbook/generators/dsl_extension/dsl_extension.rb +29 -0
  56. data/lib/runbook/generators/dsl_extension/templates/dsl_extension.tt +25 -0
  57. data/lib/runbook/generators/generator/generator.rb +43 -0
  58. data/lib/runbook/generators/generator/templates/generator.tt +53 -0
  59. data/lib/runbook/generators/project/project.rb +301 -0
  60. data/lib/runbook/generators/project/templates/Gemfile.tt +6 -0
  61. data/lib/runbook/generators/project/templates/README.md.tt +29 -0
  62. data/lib/runbook/generators/project/templates/Runbookfile.tt +11 -0
  63. data/lib/runbook/generators/project/templates/base_file.rb.tt +8 -0
  64. data/lib/runbook/generators/runbook/runbook.rb +22 -0
  65. data/lib/runbook/generators/runbook/templates/runbook.tt +20 -0
  66. data/lib/runbook/generators/statement/statement.rb +18 -0
  67. data/lib/runbook/generators/statement/templates/statement.tt +34 -0
  68. data/lib/runbook/helpers/format_helper.rb +11 -0
  69. data/lib/runbook/helpers/ssh_kit_helper.rb +143 -0
  70. data/lib/runbook/helpers/tmux_helper.rb +176 -0
  71. data/lib/runbook/hooks.rb +88 -0
  72. data/lib/runbook/initializer.rb +85 -0
  73. data/lib/runbook/node.rb +33 -0
  74. data/lib/runbook/run.rb +290 -0
  75. data/lib/runbook/runner.rb +66 -0
  76. data/lib/runbook/runs/ssh_kit.rb +193 -0
  77. data/lib/runbook/statement.rb +20 -0
  78. data/lib/runbook/statements/ask.rb +12 -0
  79. data/lib/runbook/statements/assert.rb +34 -0
  80. data/lib/runbook/statements/capture.rb +14 -0
  81. data/lib/runbook/statements/capture_all.rb +14 -0
  82. data/lib/runbook/statements/command.rb +11 -0
  83. data/lib/runbook/statements/confirm.rb +10 -0
  84. data/lib/runbook/statements/description.rb +9 -0
  85. data/lib/runbook/statements/download.rb +12 -0
  86. data/lib/runbook/statements/layout.rb +10 -0
  87. data/lib/runbook/statements/note.rb +10 -0
  88. data/lib/runbook/statements/notice.rb +10 -0
  89. data/lib/runbook/statements/ruby_command.rb +9 -0
  90. data/lib/runbook/statements/tmux_command.rb +11 -0
  91. data/lib/runbook/statements/upload.rb +12 -0
  92. data/lib/runbook/statements/wait.rb +10 -0
  93. data/lib/runbook/toolbox.rb +37 -0
  94. data/lib/runbook/util/repo.rb +57 -0
  95. data/lib/runbook/util/runbook.rb +43 -0
  96. data/lib/runbook/util/sticky_hash.rb +26 -0
  97. data/lib/runbook/util/stored_pose.rb +55 -0
  98. data/lib/runbook/version.rb +3 -0
  99. data/lib/runbook/view.rb +24 -0
  100. data/lib/runbook/viewer.rb +24 -0
  101. data/lib/runbook/views/markdown.rb +117 -0
  102. data/lib/runbook.rb +140 -0
  103. data/runbook.gemspec +53 -0
  104. metadata +419 -0
@@ -0,0 +1,58 @@
1
+ ::SSHKit::Backend::Abstract.class_eval do
2
+ # Code taken from https://github.com/capistrano/sshkit/blob/v1.16.0/lib/sshkit/backends/abstract.rb#L98-L116
3
+ # Copyright (c) 2008- Lee Hambley & Contributors
4
+ # License link: https://github.com/capistrano/sshkit/blob/v1.16.0/LICENSE.md
5
+ #
6
+ # Full copyright notice and license:
7
+ #
8
+ # Copyright (c) 2008- Lee Hambley & Contributors
9
+ #
10
+ # Permission is hereby granted, free of charge, to any person obtaining a
11
+ # copy of this software and associated documentation files (the "Software"),
12
+ # to deal in the Software without restriction, including without limitation
13
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
14
+ # and/or sell copies of the Software, and to permit persons to whom the
15
+ # Software is furnished to do so, subject to the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be included
18
+ # in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
+ # DEALINGS IN THE SOFTWARE.
27
+
28
+ def as(who, &_block)
29
+ if who.is_a? Hash
30
+ @user = who[:user] || who["user"]
31
+ @group = who[:group] || who["group"]
32
+ else
33
+ @user = who
34
+ @group = nil
35
+ end
36
+
37
+ execute_args = {verbosity: Logger::DEBUG}
38
+ old_pty = ::SSHKit::Backend::Netssh.config.pty
39
+ begin
40
+ if Runbook.configuration.enable_sudo_prompt
41
+ execute_args[:interaction_handler] ||= ::SSHKit::Sudo::InteractionHandler.new
42
+ ::SSHKit::Backend::Netssh.config.pty = true
43
+ end
44
+ execute <<-EOTEST, execute_args
45
+ if ! sudo -u #{@user} whoami > /dev/null
46
+ then echo "You cannot switch to user '#{@user}' using sudo, please check the sudoers file" 1>&2
47
+ false
48
+ fi
49
+ EOTEST
50
+ yield
51
+ ensure
52
+ ::SSHKit::Backend::Netssh.config.pty = old_pty
53
+ end
54
+ ensure
55
+ remove_instance_variable(:@user)
56
+ remove_instance_variable(:@group)
57
+ end
58
+ end
@@ -0,0 +1,25 @@
1
+ module Runbook
2
+ class AirbrusshContext
3
+ attr_reader :history, :current_task_name
4
+
5
+ def initialize(config=Airbrussh.configuration)
6
+ @history = []
7
+ end
8
+
9
+ def register_new_command(command)
10
+ hist_entry = command.to_s
11
+ first_execution = history.last != hist_entry
12
+ history << hist_entry if first_execution
13
+ first_execution
14
+ end
15
+
16
+ def position(command)
17
+ history.rindex(command.to_s)
18
+ end
19
+
20
+ def set_current_task_name(task_name)
21
+ @current_task_name = task_name
22
+ history.clear
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,115 @@
1
+ require "thor"
2
+ require "runbook"
3
+ require "runbook/cli_base"
4
+ require "runbook/initializer"
5
+
6
+ # Needed to load custom generators
7
+ Runbook::Configuration.load_config
8
+ require "runbook/generator"
9
+
10
+ module Runbook
11
+ class CLI < Thor
12
+ include ::Runbook::CLIBase
13
+
14
+ map "--version" => :__print_version
15
+
16
+ desc "view RUNBOOK", "Prints a formatted version of the runbook"
17
+ long_desc <<-LONGDESC
18
+ Prints the runbook.
19
+
20
+ With --view (-v), Prints the view using the specified view type
21
+ LONGDESC
22
+ option :view, aliases: "-v", type: :string, default: :markdown
23
+ def view(runbook)
24
+ runbook = _retrieve_runbook(runbook, :view)
25
+ puts Runbook::Viewer.new(runbook).generate(
26
+ view: options[:view],
27
+ )
28
+ end
29
+
30
+ desc "exec RUNBOOK", "Executes the runbook"
31
+ long_desc <<-LONGDESC
32
+ Executes the runbook.
33
+
34
+ With --noop (-n), Runs the runbook in no-op mode, preventing
35
+ commands from executing.
36
+
37
+ With --auto (-a), Runs the runbook in auto mode. This
38
+ will prevent the execution from asking
39
+ for any user input (such as confirmations).
40
+ Not all runbooks are compatible with auto
41
+ mode (if they use the ask statement without
42
+ defaults for example).
43
+
44
+ With --run (-r), Runs the runbook with the specified run type
45
+
46
+ With --no-paranoid (-P), Runs the runbook without prompting to
47
+ continue at every step
48
+
49
+ With --start-at (-s), Runs the runbook starting at the specified
50
+ section or step.
51
+ LONGDESC
52
+ option :run, aliases: "-r", type: :string, default: :ssh_kit
53
+ option :noop, aliases: "-n", type: :boolean
54
+ option :auto, aliases: "-a", type: :boolean
55
+ option :"no-paranoid", aliases: "-P", type: :boolean
56
+ option :start_at, aliases: "-s", type: :string, default: "0"
57
+ def exec(runbook)
58
+ runbook = _retrieve_runbook(runbook, :exec)
59
+ Runbook::Runner.new(runbook).run(
60
+ run: options[:run],
61
+ noop: options[:noop],
62
+ auto: options[:auto],
63
+ paranoid: options[:"no-paranoid"] == nil,
64
+ start_at: options[:start_at],
65
+ )
66
+ end
67
+
68
+ desc "generate GENERATOR", "Generate runbook objects from a template, such as runbooks, projects, or plugins."
69
+ long_desc <<-LONGDESC
70
+ Generates a runbook, runbook node, runbook project, or runbook plugin from a template.
71
+ LONGDESC
72
+ subcommand "generate", Runbook::Generator
73
+
74
+ desc "init", "Initialize Runbook in an existing project"
75
+ long_desc "Set up Runbook directory structure and Runbookfile in an existing project for executing runbooks."
76
+ Runbook::Initializer.class_options.values.each do |co|
77
+ method_option co.name, desc: co.description, required: co.required,
78
+ default: co.default, aliases: co.aliases, type: co.type,
79
+ banner: co.banner, hide: co.hide
80
+ end
81
+ def init
82
+ invoke(Runbook::Initializer)
83
+ end
84
+
85
+ desc "--version", "Print runbook's version"
86
+ def __print_version
87
+ puts "Runbook v#{Runbook::VERSION}"
88
+ end
89
+
90
+ private
91
+
92
+ def _retrieve_runbook(runbook, cmd)
93
+ unless File.exist?(runbook)
94
+ raise Thor::InvocationError, "#{cmd}: cannot access #{runbook}: No such file or directory"
95
+ end
96
+
97
+ begin
98
+ load(runbook)
99
+ Runbook.books.last || eval(File.read(runbook))
100
+ rescue NameError => e
101
+ if Runbook.runtime_methods.include?(e.name)
102
+ message = (
103
+ "Runtime method `#{e.name}` cannot be referenced at " \
104
+ "compile time. Wrap statements referencing it in a " \
105
+ "`ruby_command` block in order to invoke the code at " \
106
+ "runtime."
107
+ )
108
+ raise e, message, e.backtrace
109
+ end
110
+
111
+ raise e
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,38 @@
1
+ module Runbook::CLIBase
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+
5
+ base.check_unknown_options!
6
+
7
+ base.class_option(
8
+ :config,
9
+ aliases: "-c",
10
+ type: :string,
11
+ group: :base,
12
+ desc: "Path to runbook config file"
13
+ )
14
+ end
15
+
16
+ def initialize(args = [], local_options = {}, config = {})
17
+ super(args, local_options, config)
18
+
19
+ cmd_name = config[:current_command].name
20
+ _set_cli_config(options[:config], cmd_name) if options[:config]
21
+ end
22
+
23
+ module ClassMethods
24
+ def exit_on_failure?
25
+ true
26
+ end
27
+ end
28
+
29
+ protected
30
+
31
+ def _set_cli_config(config, cmd)
32
+ unless File.exist?(config)
33
+ raise Thor::InvocationError, "#{cmd}: cannot access #{config}: No such file or directory"
34
+ end
35
+ Runbook::Configuration.cli_config_file = config
36
+ Runbook::Configuration.reconfigure
37
+ end
38
+ end
@@ -0,0 +1,120 @@
1
+ module Runbook
2
+ class << self
3
+ attr_accessor :configuration
4
+
5
+ def config
6
+ @configuration
7
+ end
8
+ end
9
+
10
+ def self.configure
11
+ Configuration.load_config
12
+ self.configuration ||= Configuration.new
13
+ yield(configuration) if block_given?
14
+ end
15
+
16
+ def self.reset_configuration
17
+ self.configuration = Configuration.new
18
+ Configuration.loaded = false
19
+ end
20
+
21
+ class Configuration
22
+ attr_accessor :_airbrussh_context
23
+ attr_accessor :ssh_kit
24
+ attr_accessor :enable_sudo_prompt
25
+ attr_reader :use_same_sudo_password
26
+
27
+ GlobalConfigFile = "/etc/runbook.conf"
28
+ ProjectConfigFile = "Runbookfile"
29
+ UserConfigFile = ".runbook.conf"
30
+
31
+ def self.cli_config_file
32
+ @cli_config_file
33
+ end
34
+
35
+ def self.cli_config_file=(cli_config_file)
36
+ @cli_config_file = cli_config_file
37
+ end
38
+
39
+ def self.loaded
40
+ @loaded
41
+ end
42
+
43
+ def self.loaded=(loaded)
44
+ @loaded = loaded
45
+ end
46
+
47
+ def self.load_config
48
+ return if @loaded
49
+ @loaded = true
50
+ _load_global_config
51
+ _load_project_config
52
+ _load_user_config
53
+ _load_cli_config
54
+ # Set defaults
55
+ Runbook.configure
56
+ end
57
+
58
+ def self.reconfigure
59
+ @loaded = false
60
+ load_config
61
+ end
62
+
63
+ def self._load_global_config
64
+ load(GlobalConfigFile) if File.exist?(GlobalConfigFile)
65
+ end
66
+
67
+ def self._load_project_config
68
+ dir = Dir.pwd
69
+ loop do
70
+ config_path = File.join(dir, ProjectConfigFile)
71
+ if File.exist?(config_path)
72
+ load(config_path)
73
+ return
74
+ end
75
+ break if File.identical?(dir, "/")
76
+ dir = File.join(dir, "..")
77
+ end
78
+ end
79
+
80
+ def self._load_user_config
81
+ user_config_file = File.join(ENV["HOME"], UserConfigFile)
82
+ load(user_config_file) if File.exist?(user_config_file)
83
+ end
84
+
85
+ def self._load_cli_config
86
+ if cli_config_file && File.exist?(cli_config_file)
87
+ load(cli_config_file)
88
+ end
89
+ end
90
+
91
+ def initialize
92
+ self.ssh_kit = SSHKit.config
93
+ formatter = Airbrussh::Formatter.new(
94
+ $stdout,
95
+ banner: nil,
96
+ command_output: true,
97
+ context: AirbrusshContext,
98
+ )
99
+ ssh_kit.output = formatter
100
+ self._airbrussh_context = formatter.formatters.find do |fmt|
101
+ fmt.is_a?(Airbrussh::ConsoleFormatter)
102
+ end.context
103
+ self.enable_sudo_prompt = true
104
+ self.use_same_sudo_password = true
105
+ end
106
+
107
+ def use_same_sudo_password=(use_same_pwd)
108
+ @use_same_sudo_password = use_same_pwd
109
+ SSHKit::Sudo::InteractionHandler.class_eval do
110
+ if use_same_pwd
111
+ use_same_password!
112
+ else
113
+ def password_cache_key(host)
114
+ "#{host.user}@#{host.hostname}"
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,21 @@
1
+ module Runbook
2
+ module DSL
3
+ def self.class(*modules)
4
+ Class.new do
5
+ attr_reader :parent
6
+
7
+ def initialize(parent)
8
+ @parent = parent
9
+ end
10
+
11
+ modules.each do |mod|
12
+ prepend mod
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.dsl_ivars
18
+ [:@parent]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Runbook::Entities
2
+ class Book < Runbook::Entity
3
+ def initialize(title, tags: [], labels: {})
4
+ super(title, tags: tags, labels: labels)
5
+ end
6
+
7
+ # Seed data for 'render' tree traversal method
8
+ def self.initial_render_metadata
9
+ {depth: 1, index: 0}
10
+ end
11
+
12
+ # Seed data for 'run' tree traversal method
13
+ def self.initial_run_metadata
14
+ {depth: 1, index: 0, position: ""}
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ module Runbook::Entities
2
+ class Section < Runbook::Entity
3
+ def initialize(title, tags: [], labels: {})
4
+ super(title, tags: tags, labels: labels)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Runbook::Entities
2
+ class Setup < Runbook::Entity
3
+ def initialize(tags: [], labels: {})
4
+ super("Setup", tags: tags, labels: labels)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Runbook::Entities
2
+ class Step < Runbook::Entity
3
+ def initialize(title=nil, tags: [], labels: {})
4
+ super(title, tags: tags, labels: labels)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,129 @@
1
+ module Runbook
2
+ class Entity < Node
3
+ include Runbook::Hooks::Invoker
4
+ const_set(:DSL, Runbook::DSL.class)
5
+
6
+ def self.inherited(child_class)
7
+ child_class.const_set(:DSL, Runbook::DSL.class)
8
+ end
9
+
10
+ attr_reader :title, :tags, :labels, :dsl
11
+
12
+ def initialize(title, tags: [], labels: {}, parent: nil)
13
+ @title = title
14
+ @tags = tags
15
+ @labels = labels
16
+ @parent = parent
17
+ @dsl = "#{self.class}::DSL".constantize.new(self)
18
+ end
19
+
20
+ def add(item)
21
+ items << item
22
+ item.parent = self
23
+ end
24
+
25
+ def items
26
+ @items ||= []
27
+ end
28
+
29
+ ruby2_keywords def method_missing(method, *args, &block)
30
+ if dsl.respond_to?(method)
31
+ dsl.send(method, *args, &block)
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ def respond_to?(name, include_private = false)
38
+ !!(dsl.respond_to?(name) || super)
39
+ end
40
+
41
+ def render(view, output, metadata)
42
+ invoke_with_hooks(view, self, output, metadata) do
43
+ view.render(self, output, metadata)
44
+ items.each_with_index do |item, index|
45
+ new_metadata = _render_metadata(items, item, metadata, index)
46
+ item.render(view, output, new_metadata)
47
+ end
48
+ end
49
+ end
50
+
51
+ def run(run, metadata)
52
+ return if _should_reverse?(run, metadata)
53
+ return if dynamic? && visited?
54
+
55
+ invoke_with_hooks(run, self, metadata) do
56
+ run.execute(self, metadata)
57
+ next if _should_reverse?(run, metadata)
58
+ loop do
59
+ items.each_with_index do |item, index|
60
+ new_metadata = _run_metadata(items, item, metadata, index)
61
+ # Optimization
62
+ break if _should_reverse?(run, new_metadata)
63
+ item.run(run, new_metadata)
64
+ end
65
+
66
+ if _should_retraverse?(run, metadata)
67
+ metadata[:reverse] = false
68
+ else
69
+ break
70
+ end
71
+ end
72
+ end
73
+ self.visited!
74
+ end
75
+
76
+ def dynamic!
77
+ items.each(&:dynamic!)
78
+ @dynamic = true
79
+ end
80
+
81
+ def _render_metadata(items, item, metadata, index)
82
+ index = items.select do |item|
83
+ item.is_a?(Entity)
84
+ end.index(item)
85
+
86
+ metadata.merge(
87
+ {
88
+ depth: metadata[:depth] + 1,
89
+ index: index,
90
+ }
91
+ )
92
+ end
93
+
94
+ def _run_metadata(items, item, metadata, index)
95
+ pos_index = items.select do |item|
96
+ item.is_a?(Entity) &&
97
+ !item.is_a?(Runbook::Entities::Setup)
98
+ end.index(item)
99
+
100
+ if pos_index
101
+ if metadata[:position].empty?
102
+ pos = "#{pos_index + 1}"
103
+ else
104
+ pos = "#{metadata[:position]}.#{pos_index + 1}"
105
+ end
106
+ else
107
+ pos = metadata[:position]
108
+ end
109
+
110
+ metadata.merge(
111
+ {
112
+ depth: metadata[:depth] + 1,
113
+ index: index,
114
+ position: pos,
115
+ }
116
+ )
117
+ end
118
+
119
+ def _should_reverse?(run, metadata)
120
+ return false unless metadata[:reverse]
121
+ run.past_position?(metadata[:position], metadata[:start_at])
122
+ end
123
+
124
+ def _should_retraverse?(run, metadata)
125
+ return false unless metadata[:reverse]
126
+ run.start_at_is_substep?(self, metadata)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,7 @@
1
+ module Runbook
2
+ StandardError = Class.new(::StandardError)
3
+
4
+ class Runner
5
+ ExecutionError = Class.new(Runbook::StandardError)
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ module Runbook::Extensions
2
+ module Add
3
+ module DSL
4
+ def add(entity)
5
+ parent.add(entity)
6
+ end
7
+ end
8
+ end
9
+
10
+ Runbook::Entities::Book::DSL.prepend(Add::DSL)
11
+ Runbook::Entities::Section::DSL.prepend(Add::DSL)
12
+ Runbook::Entities::Setup::DSL.prepend(Add::DSL)
13
+ Runbook::Entities::Step::DSL.prepend(Add::DSL)
14
+ end
@@ -0,0 +1,14 @@
1
+ module Runbook::Extensions
2
+ module Description
3
+ module DSL
4
+ def description(msg)
5
+ Runbook::Statements::Description.new(msg).tap do |desc|
6
+ parent.add(desc)
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ Runbook::Entities::Book::DSL.prepend(Description::DSL)
13
+ Runbook::Entities::Section::DSL.prepend(Description::DSL)
14
+ end
@@ -0,0 +1,19 @@
1
+ module Runbook::Extensions
2
+ module Sections
3
+ module DSL
4
+ def section(title, *tags, labels: {}, &block)
5
+ Runbook::Entities::Section.new(
6
+ title,
7
+ tags: tags,
8
+ labels: labels,
9
+ ).tap do |section|
10
+ parent.add(section)
11
+ section.dsl.instance_eval(&block)
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ Runbook::Entities::Book::DSL.prepend(Sections::DSL)
18
+ Runbook::Entities::Section::DSL.prepend(Sections::DSL)
19
+ end
@@ -0,0 +1,17 @@
1
+ module Runbook::Extensions
2
+ module Setup
3
+ module DSL
4
+ def setup(*tags, labels: {}, &block)
5
+ Runbook::Entities::Setup.new(
6
+ tags: tags,
7
+ labels: labels,
8
+ ).tap do |setup|
9
+ parent.add(setup)
10
+ setup.dsl.instance_eval(&block) if block
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ Runbook::Entities::Book::DSL.prepend(Setup::DSL)
17
+ end
@@ -0,0 +1,51 @@
1
+ module Runbook::Extensions
2
+ module SharedVariables
3
+ module RunHooks
4
+ def self.register_shared_variables_hooks(base)
5
+ base.register_hook(
6
+ :set_ivars_hook,
7
+ :before,
8
+ Runbook::Statement,
9
+ ) do |object, metadata|
10
+ target = SharedVariables::RunHooks._target(object)
11
+ metadata[:repo].each do |key, value|
12
+ target.singleton_class.class_eval { attr_accessor key }
13
+ target.send(SharedVariables::RunHooks._eqls_method(key), value)
14
+ end
15
+ end
16
+
17
+ base.register_hook(
18
+ :copy_ivars_to_repo_hook,
19
+ :after,
20
+ Runbook::Statement,
21
+ before: :save_repo_hook
22
+ ) do |object, metadata|
23
+ SharedVariables::RunHooks._copy_ivars_to_repo(object, metadata)
24
+ end
25
+ end
26
+
27
+ def self._copy_ivars_to_repo(object, metadata)
28
+ target = _target(object)
29
+ ivars = target.instance_variables - Runbook::DSL.dsl_ivars
30
+
31
+ ivars.each do |ivar|
32
+ repo_key = ivar.to_s[1..-1].to_sym
33
+ val = target.instance_variable_get(ivar)
34
+ metadata[:repo][repo_key] = val
35
+ end
36
+ end
37
+
38
+ def self._target(object)
39
+ object.parent.dsl
40
+ end
41
+
42
+ def self._eqls_method(key)
43
+ "#{key}=".to_sym
44
+ end
45
+ end
46
+ end
47
+
48
+ Runbook.runs.each do |run|
49
+ SharedVariables::RunHooks.register_shared_variables_hooks(run)
50
+ end
51
+ end