careacademy-runbook 1.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.
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