runbook 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +46 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +999 -0
- data/Rakefile +6 -0
- data/TODO.md +38 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/runbook +5 -0
- data/images/runbook_anatomy_diagram.png +0 -0
- data/images/runbook_example.gif +0 -0
- data/images/runbook_execution_modes.png +0 -0
- data/lib/hacks/ssh_kit.rb +58 -0
- data/lib/runbook/cli.rb +90 -0
- data/lib/runbook/configuration.rb +110 -0
- data/lib/runbook/dsl.rb +21 -0
- data/lib/runbook/entities/book.rb +17 -0
- data/lib/runbook/entities/section.rb +7 -0
- data/lib/runbook/entities/step.rb +7 -0
- data/lib/runbook/entity.rb +127 -0
- data/lib/runbook/errors.rb +7 -0
- data/lib/runbook/extensions/add.rb +13 -0
- data/lib/runbook/extensions/description.rb +14 -0
- data/lib/runbook/extensions/sections.rb +15 -0
- data/lib/runbook/extensions/shared_variables.rb +51 -0
- data/lib/runbook/extensions/ssh_config.rb +76 -0
- data/lib/runbook/extensions/statements.rb +26 -0
- data/lib/runbook/extensions/steps.rb +14 -0
- data/lib/runbook/extensions/tmux.rb +13 -0
- data/lib/runbook/helpers/format_helper.rb +11 -0
- data/lib/runbook/helpers/ssh_kit_helper.rb +143 -0
- data/lib/runbook/helpers/tmux_helper.rb +174 -0
- data/lib/runbook/hooks.rb +88 -0
- data/lib/runbook/node.rb +23 -0
- data/lib/runbook/run.rb +283 -0
- data/lib/runbook/runner.rb +64 -0
- data/lib/runbook/runs/ssh_kit.rb +186 -0
- data/lib/runbook/statement.rb +22 -0
- data/lib/runbook/statements/ask.rb +11 -0
- data/lib/runbook/statements/assert.rb +25 -0
- data/lib/runbook/statements/capture.rb +14 -0
- data/lib/runbook/statements/capture_all.rb +14 -0
- data/lib/runbook/statements/command.rb +11 -0
- data/lib/runbook/statements/confirm.rb +10 -0
- data/lib/runbook/statements/description.rb +9 -0
- data/lib/runbook/statements/download.rb +12 -0
- data/lib/runbook/statements/layout.rb +10 -0
- data/lib/runbook/statements/note.rb +10 -0
- data/lib/runbook/statements/notice.rb +10 -0
- data/lib/runbook/statements/ruby_command.rb +9 -0
- data/lib/runbook/statements/tmux_command.rb +11 -0
- data/lib/runbook/statements/upload.rb +12 -0
- data/lib/runbook/statements/wait.rb +10 -0
- data/lib/runbook/toolbox.rb +43 -0
- data/lib/runbook/util/repo.rb +56 -0
- data/lib/runbook/util/runbook.rb +25 -0
- data/lib/runbook/util/sticky_hash.rb +26 -0
- data/lib/runbook/util/stored_pose.rb +54 -0
- data/lib/runbook/version.rb +3 -0
- data/lib/runbook/view.rb +24 -0
- data/lib/runbook/viewer.rb +24 -0
- data/lib/runbook/views/markdown.rb +109 -0
- data/lib/runbook.rb +110 -0
- data/runbook.gemspec +48 -0
- data/samples/hooks_runbook.rb +72 -0
- data/samples/layout_runbook.rb +26 -0
- data/samples/restart_nginx.rb +26 -0
- data/samples/simple_runbook.rb +41 -0
- metadata +324 -0
data/Rakefile
ADDED
data/TODO.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
## Desired feature list
|
2
|
+
* [X] Revise README.md
|
3
|
+
* [] capistrano-runbook gem that integrates runbook into capistrano tasks
|
4
|
+
* [] Create a generator for a runbook? Allow for custom generators?
|
5
|
+
* Generate runbook templates
|
6
|
+
* Generate runbook projects with Runbookfile, Gemfile, etc.
|
7
|
+
* Generate plugins
|
8
|
+
* [] Add Appraisal to test against multiple versions of Ruby
|
9
|
+
* [] Update output to be more log friendly, including timestamps for operations
|
10
|
+
* [] Allow for preventing echo when prompting for input
|
11
|
+
* [] Add an ability for skipping mutation commands that will have an affect on the system
|
12
|
+
* [] Add a revert section that does not get executed, but can be executed by passing a revert flag
|
13
|
+
* [] Specify version in runbook to allow for supporting backwards incompatible runbook DSL format changes
|
14
|
+
* [] Add support for sudo interaction handler for raw commands
|
15
|
+
* [] Replace Thor with a solution that is more easily extendable (adding new flags, etc.)
|
16
|
+
* [] Add goto statements for repeating steps (functionality exists in paranoid mode)
|
17
|
+
* [] Add test statement
|
18
|
+
* [] Feedback on completion of tmux commands (when they complete, return values, outputs)
|
19
|
+
* [] Add shorter aliases for tmux layout keys
|
20
|
+
* [] Add host aliases for ssh_config setters
|
21
|
+
* [] Allow for step dependencies that get executed before the step
|
22
|
+
* [] Add periodic flush for sshkit output
|
23
|
+
* Add a way to execute a series of commands in groups
|
24
|
+
* Docker testing story for more full-stack integration tests
|
25
|
+
* Test integration with sshkit
|
26
|
+
* Test sshkit-sudo functionality
|
27
|
+
* logging solution for alternate output
|
28
|
+
* Pattern for conditionally enabling plugins? Conditional plugins should be implemented as separate gems.
|
29
|
+
* Requiring a plugin is the same as enabling it
|
30
|
+
* Configuration can be added to toggle aspects of the plugin
|
31
|
+
* Add a setup step that always executes even if start_at is defined
|
32
|
+
* Yaml specification format (subset of Ruby)
|
33
|
+
* Will not contain as much flexibility as Ruby format
|
34
|
+
* Can convert from Ruby format to yaml (depending on compatibility) and yaml to Ruby
|
35
|
+
* Guard for view updates (How to handle arguments?)
|
36
|
+
* Be able to serve up markdown docs (web server) for easy viewing
|
37
|
+
* Could provide a rake task for compiling and nooping runbooks?
|
38
|
+
* Can specify input-format, output-format, input (file), and output (file)
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "runbook"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
require "pry"
|
11
|
+
Pry.start
|
12
|
+
|
13
|
+
# require "irb"
|
14
|
+
# IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/exe/runbook
ADDED
Binary file
|
Binary file
|
Binary file
|
@@ -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
|
data/lib/runbook/cli.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require "thor"
|
2
|
+
require "runbook"
|
3
|
+
|
4
|
+
module Runbook
|
5
|
+
class CLI < Thor
|
6
|
+
map "--version" => :__print_version
|
7
|
+
class_option :config, aliases: :c, type: :string
|
8
|
+
|
9
|
+
def initialize(args = [], local_options = {}, config = {})
|
10
|
+
super(args, local_options, config)
|
11
|
+
|
12
|
+
cmd_name = config[:current_command].name
|
13
|
+
_set_cli_config(options[:config], cmd_name) if options[:config]
|
14
|
+
end
|
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 "--version", "Print runbook's version"
|
69
|
+
def __print_version
|
70
|
+
puts "Runbook v#{Runbook::VERSION}"
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def _set_cli_config(config, cmd)
|
76
|
+
unless File.exist?(config)
|
77
|
+
raise Thor::InvocationError, "#{cmd}: cannot access #{config}: No such file or directory"
|
78
|
+
end
|
79
|
+
Runbook::Configuration.cli_config_file = config
|
80
|
+
end
|
81
|
+
|
82
|
+
def _retrieve_runbook(runbook, cmd)
|
83
|
+
unless File.exist?(runbook)
|
84
|
+
raise Thor::InvocationError, "#{cmd}: cannot access #{runbook}: No such file or directory"
|
85
|
+
end
|
86
|
+
load(runbook)
|
87
|
+
Runbook.books.last || eval(File.read(runbook))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Runbook
|
2
|
+
class << self
|
3
|
+
attr_accessor :configuration
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.configure
|
7
|
+
Configuration.load_config
|
8
|
+
self.configuration ||= Configuration.new
|
9
|
+
yield(configuration) if block_given?
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.reset_configuration
|
13
|
+
self.configuration = Configuration.new
|
14
|
+
Configuration.loaded = false
|
15
|
+
end
|
16
|
+
|
17
|
+
class Configuration
|
18
|
+
attr_accessor :ssh_kit
|
19
|
+
attr_accessor :enable_sudo_prompt
|
20
|
+
attr_reader :use_same_sudo_password
|
21
|
+
|
22
|
+
GlobalConfigFile = "/etc/runbook.conf"
|
23
|
+
ProjectConfigFile = "Runbookfile"
|
24
|
+
UserConfigFile = ".runbook.conf"
|
25
|
+
|
26
|
+
def self.cli_config_file
|
27
|
+
@cli_config_file
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.cli_config_file=(cli_config_file)
|
31
|
+
@cli_config_file = cli_config_file
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.loaded
|
35
|
+
@loaded
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.loaded=(loaded)
|
39
|
+
@loaded = loaded
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.load_config
|
43
|
+
return if @loaded
|
44
|
+
@loaded = true
|
45
|
+
_load_global_config
|
46
|
+
_load_project_config
|
47
|
+
_load_user_config
|
48
|
+
_load_cli_config
|
49
|
+
# Set defaults
|
50
|
+
Runbook.configure
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.reconfigure
|
54
|
+
@loaded = false
|
55
|
+
load_config
|
56
|
+
end
|
57
|
+
|
58
|
+
def self._load_global_config
|
59
|
+
load(GlobalConfigFile) if File.exist?(GlobalConfigFile)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self._load_project_config
|
63
|
+
dir = Dir.pwd
|
64
|
+
loop do
|
65
|
+
config_path = File.join(dir, ProjectConfigFile)
|
66
|
+
if File.exist?(config_path)
|
67
|
+
load(config_path)
|
68
|
+
return
|
69
|
+
end
|
70
|
+
break if File.identical?(dir, "/")
|
71
|
+
dir = File.join(dir, "..")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self._load_user_config
|
76
|
+
user_config_file = File.join(ENV["HOME"], UserConfigFile)
|
77
|
+
load(user_config_file) if File.exist?(user_config_file)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self._load_cli_config
|
81
|
+
if cli_config_file && File.exist?(cli_config_file)
|
82
|
+
load(cli_config_file)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def initialize
|
87
|
+
self.ssh_kit = SSHKit.config
|
88
|
+
ssh_kit.output = Airbrussh::Formatter.new(
|
89
|
+
$stdout,
|
90
|
+
banner: nil,
|
91
|
+
command_output: true,
|
92
|
+
)
|
93
|
+
self.enable_sudo_prompt = true
|
94
|
+
self.use_same_sudo_password = true
|
95
|
+
end
|
96
|
+
|
97
|
+
def use_same_sudo_password=(use_same_pwd)
|
98
|
+
@use_same_sudo_password = use_same_pwd
|
99
|
+
SSHKit::Sudo::InteractionHandler.class_eval do
|
100
|
+
if use_same_pwd
|
101
|
+
use_same_password!
|
102
|
+
else
|
103
|
+
def password_cache_key(host)
|
104
|
+
"#{host.user}@#{host.hostname}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/runbook/dsl.rb
ADDED
@@ -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)
|
4
|
+
super(title)
|
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,127 @@
|
|
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_accessor :parent
|
11
|
+
attr_reader :title, :dsl
|
12
|
+
|
13
|
+
def initialize(title, parent: nil)
|
14
|
+
@title = title
|
15
|
+
@parent = parent
|
16
|
+
@dsl = "#{self.class}::DSL".constantize.new(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(item)
|
20
|
+
items << item
|
21
|
+
item.parent = self
|
22
|
+
end
|
23
|
+
|
24
|
+
def items
|
25
|
+
@items ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(method, *args, &block)
|
29
|
+
if dsl.respond_to?(method)
|
30
|
+
dsl.send(method, *args, &block)
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def respond_to?(name, include_private = false)
|
37
|
+
!!(dsl.respond_to?(name) || super)
|
38
|
+
end
|
39
|
+
|
40
|
+
def render(view, output, metadata)
|
41
|
+
invoke_with_hooks(view, self, output, metadata) do
|
42
|
+
view.render(self, output, metadata)
|
43
|
+
items.each_with_index do |item, index|
|
44
|
+
new_metadata = _render_metadata(items, item, metadata, index)
|
45
|
+
item.render(view, output, new_metadata)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def run(run, metadata)
|
51
|
+
return if _should_reverse?(run, metadata)
|
52
|
+
return if dynamic? && visited?
|
53
|
+
|
54
|
+
invoke_with_hooks(run, self, metadata) do
|
55
|
+
run.execute(self, metadata)
|
56
|
+
next if _should_reverse?(run, metadata)
|
57
|
+
loop do
|
58
|
+
items.each_with_index do |item, index|
|
59
|
+
new_metadata = _run_metadata(items, item, metadata, index)
|
60
|
+
# Optimization
|
61
|
+
break if _should_reverse?(run, new_metadata)
|
62
|
+
item.run(run, new_metadata)
|
63
|
+
end
|
64
|
+
|
65
|
+
if _should_retraverse?(run, metadata)
|
66
|
+
metadata[:reverse] = false
|
67
|
+
else
|
68
|
+
break
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
self.visited!
|
73
|
+
end
|
74
|
+
|
75
|
+
def dynamic!
|
76
|
+
items.each(&:dynamic!)
|
77
|
+
@dynamic = true
|
78
|
+
end
|
79
|
+
|
80
|
+
def _render_metadata(items, item, metadata, index)
|
81
|
+
index = items.select do |item|
|
82
|
+
item.is_a?(Entity)
|
83
|
+
end.index(item)
|
84
|
+
|
85
|
+
metadata.merge(
|
86
|
+
{
|
87
|
+
depth: metadata[:depth] + 1,
|
88
|
+
index: index,
|
89
|
+
}
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
def _run_metadata(items, item, metadata, index)
|
94
|
+
pos_index = items.select do |item|
|
95
|
+
item.is_a?(Entity)
|
96
|
+
end.index(item)
|
97
|
+
|
98
|
+
if pos_index
|
99
|
+
if metadata[:position].empty?
|
100
|
+
pos = "#{pos_index + 1}"
|
101
|
+
else
|
102
|
+
pos = "#{metadata[:position]}.#{pos_index + 1}"
|
103
|
+
end
|
104
|
+
else
|
105
|
+
pos = metadata[:position]
|
106
|
+
end
|
107
|
+
|
108
|
+
metadata.merge(
|
109
|
+
{
|
110
|
+
depth: metadata[:depth] + 1,
|
111
|
+
index: index,
|
112
|
+
position: pos,
|
113
|
+
}
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
def _should_reverse?(run, metadata)
|
118
|
+
return false unless metadata[:reverse]
|
119
|
+
run.past_position?(metadata[:position], metadata[:start_at])
|
120
|
+
end
|
121
|
+
|
122
|
+
def _should_retraverse?(run, metadata)
|
123
|
+
return false unless metadata[:reverse]
|
124
|
+
run.start_at_is_substep?(self, metadata)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,13 @@
|
|
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::Step::DSL.prepend(Add::DSL)
|
13
|
+
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,15 @@
|
|
1
|
+
module Runbook::Extensions
|
2
|
+
module Sections
|
3
|
+
module DSL
|
4
|
+
def section(title, &block)
|
5
|
+
Runbook::Entities::Section.new(title).tap do |section|
|
6
|
+
parent.add(section)
|
7
|
+
section.dsl.instance_eval(&block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Runbook::Entities::Book::DSL.prepend(Sections::DSL)
|
14
|
+
Runbook::Entities::Section::DSL.prepend(Sections::DSL)
|
15
|
+
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
|