runbook 0.14.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +17 -0
- data/.gitignore +4 -0
- data/.ruby-version +1 -1
- data/.travis.yml +23 -7
- data/Appraisals +8 -0
- data/CHANGELOG.md +71 -0
- data/README.md +159 -25
- data/Rakefile +7 -1
- data/TODO.md +316 -46
- data/dockerfiles/Dockerfile-runbook +18 -0
- data/dockerfiles/Dockerfile-sshd +4 -0
- data/{samples → examples}/hooks_runbook.rb +0 -0
- data/{samples → examples}/layout_runbook.rb +0 -0
- data/{samples → examples}/restart_nginx.rb +0 -0
- data/{samples → examples}/simple_runbook.rb +0 -0
- data/examples/suppress_capture_output.rb +47 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/activesupport_5.gemfile +7 -0
- data/gemfiles/activesupport_6.gemfile +7 -0
- data/lib/runbook.rb +28 -6
- data/lib/runbook/airbrussh_context.rb +25 -0
- data/lib/runbook/cli.rb +17 -13
- data/lib/runbook/configuration.rb +7 -1
- data/lib/runbook/entities/book.rb +2 -2
- data/lib/runbook/entities/section.rb +2 -2
- data/lib/runbook/entities/setup.rb +7 -0
- data/lib/runbook/entities/step.rb +2 -2
- data/lib/runbook/entity.rb +7 -5
- data/lib/runbook/extensions/add.rb +1 -0
- data/lib/runbook/extensions/sections.rb +6 -2
- data/lib/runbook/extensions/setup.rb +17 -0
- data/lib/runbook/extensions/ssh_config.rb +2 -0
- data/lib/runbook/extensions/statements.rb +6 -1
- data/lib/runbook/extensions/steps.rb +12 -2
- data/lib/runbook/generators/project/project.rb +32 -9
- data/lib/runbook/helpers/tmux_helper.rb +6 -4
- data/lib/runbook/node.rb +10 -0
- data/lib/runbook/run.rb +14 -8
- data/lib/runbook/runner.rb +2 -0
- data/lib/runbook/runs/ssh_kit.rb +20 -13
- data/lib/runbook/statement.rb +0 -2
- data/lib/runbook/statements/ask.rb +3 -2
- data/lib/runbook/statements/assert.rb +11 -2
- data/lib/runbook/toolbox.rb +3 -9
- data/lib/runbook/util/repo.rb +4 -3
- data/lib/runbook/util/runbook.rb +4 -0
- data/lib/runbook/util/stored_pose.rb +4 -3
- data/lib/runbook/version.rb +1 -1
- data/lib/runbook/views/markdown.rb +15 -7
- data/runbook.gemspec +12 -8
- metadata +108 -29
data/lib/runbook.rb
CHANGED
@@ -2,8 +2,10 @@ require "tmpdir"
|
|
2
2
|
require "yaml"
|
3
3
|
require "thread"
|
4
4
|
|
5
|
+
require "active_support/deprecation"
|
5
6
|
require "active_support/inflector"
|
6
7
|
require "method_source"
|
8
|
+
require "ruby2_keywords"
|
7
9
|
require "pastel"
|
8
10
|
require "sshkit"
|
9
11
|
require "sshkit/sudo"
|
@@ -12,6 +14,8 @@ require "tty-progressbar"
|
|
12
14
|
require "tty-prompt"
|
13
15
|
require "thor/group"
|
14
16
|
|
17
|
+
require "runbook/airbrussh_context"
|
18
|
+
|
15
19
|
require "runbook/configuration"
|
16
20
|
|
17
21
|
require "hacks/ssh_kit"
|
@@ -30,6 +34,7 @@ require "runbook/node"
|
|
30
34
|
require "runbook/entity"
|
31
35
|
require "runbook/entities/book"
|
32
36
|
require "runbook/entities/section"
|
37
|
+
require "runbook/entities/setup"
|
33
38
|
require "runbook/entities/step"
|
34
39
|
|
35
40
|
require "runbook/statement"
|
@@ -74,6 +79,7 @@ require "runbook/extensions/add"
|
|
74
79
|
require "runbook/extensions/description"
|
75
80
|
require "runbook/extensions/shared_variables"
|
76
81
|
require "runbook/extensions/sections"
|
82
|
+
require "runbook/extensions/setup"
|
77
83
|
require "runbook/extensions/ssh_config"
|
78
84
|
require "runbook/extensions/statements"
|
79
85
|
require "runbook/extensions/steps"
|
@@ -86,24 +92,36 @@ ActiveSupport::Inflector.inflections(:en) do |inflect|
|
|
86
92
|
end
|
87
93
|
|
88
94
|
module Runbook
|
89
|
-
def self.book(title, &block)
|
95
|
+
def self.book(title, *tags, labels: {}, &block)
|
90
96
|
Configuration.load_config
|
91
|
-
Entities::Book.new(title).tap do |book|
|
97
|
+
Entities::Book.new(title, tags: tags, labels: labels).tap do |book|
|
92
98
|
book.dsl.instance_eval(&block)
|
93
99
|
register(book)
|
94
100
|
end
|
95
101
|
end
|
96
102
|
|
97
|
-
def self.section(title, &block)
|
103
|
+
def self.section(title, *tags, labels: {}, &block)
|
98
104
|
Configuration.load_config
|
99
|
-
Entities::Section.new(title).tap do |section|
|
105
|
+
Entities::Section.new(title, tags: tags, labels: labels).tap do |section|
|
100
106
|
section.dsl.instance_eval(&block)
|
101
107
|
end
|
102
108
|
end
|
103
109
|
|
104
|
-
def self.
|
110
|
+
def self.setup(*tags, labels: {}, &block)
|
105
111
|
Configuration.load_config
|
106
|
-
Entities::
|
112
|
+
Entities::Setup.new(tags: tags, labels: labels).tap do |setup|
|
113
|
+
setup.dsl.instance_eval(&block)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.step(title=nil, *tags, labels: {}, &block)
|
118
|
+
if title.is_a?(Symbol)
|
119
|
+
tags.unshift(title)
|
120
|
+
title = nil
|
121
|
+
end
|
122
|
+
|
123
|
+
Configuration.load_config
|
124
|
+
Entities::Step.new(title, tags: tags, labels: labels).tap do |step|
|
107
125
|
step.dsl.instance_eval(&block) if block
|
108
126
|
end
|
109
127
|
end
|
@@ -115,4 +133,8 @@ module Runbook
|
|
115
133
|
def self.books
|
116
134
|
@books ||= []
|
117
135
|
end
|
136
|
+
|
137
|
+
def self.runtime_methods
|
138
|
+
@runtime_methods ||= []
|
139
|
+
end
|
118
140
|
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
|
data/lib/runbook/cli.rb
CHANGED
@@ -82,17 +82,6 @@ module Runbook
|
|
82
82
|
invoke(Runbook::Initializer)
|
83
83
|
end
|
84
84
|
|
85
|
-
desc "install", "Install Runbook in an existing project", hide: true
|
86
|
-
Runbook::Initializer.class_options.values.each do |co|
|
87
|
-
method_option co.name, desc: co.description, required: co.required,
|
88
|
-
default: co.default, aliases: co.aliases, type: co.type,
|
89
|
-
banner: co.banner, hide: co.hide
|
90
|
-
end
|
91
|
-
def install
|
92
|
-
Runbook.deprecator.deprecation_warning(:install, :init)
|
93
|
-
invoke(Runbook::Initializer)
|
94
|
-
end
|
95
|
-
|
96
85
|
desc "--version", "Print runbook's version"
|
97
86
|
def __print_version
|
98
87
|
puts "Runbook v#{Runbook::VERSION}"
|
@@ -104,8 +93,23 @@ module Runbook
|
|
104
93
|
unless File.exist?(runbook)
|
105
94
|
raise Thor::InvocationError, "#{cmd}: cannot access #{runbook}: No such file or directory"
|
106
95
|
end
|
107
|
-
|
108
|
-
|
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
|
109
113
|
end
|
110
114
|
end
|
111
115
|
end
|
@@ -19,6 +19,7 @@ module Runbook
|
|
19
19
|
end
|
20
20
|
|
21
21
|
class Configuration
|
22
|
+
attr_accessor :_airbrussh_context
|
22
23
|
attr_accessor :ssh_kit
|
23
24
|
attr_accessor :enable_sudo_prompt
|
24
25
|
attr_reader :use_same_sudo_password
|
@@ -89,11 +90,16 @@ module Runbook
|
|
89
90
|
|
90
91
|
def initialize
|
91
92
|
self.ssh_kit = SSHKit.config
|
92
|
-
|
93
|
+
formatter = Airbrussh::Formatter.new(
|
93
94
|
$stdout,
|
94
95
|
banner: nil,
|
95
96
|
command_output: true,
|
97
|
+
context: AirbrusshContext,
|
96
98
|
)
|
99
|
+
ssh_kit.output = formatter
|
100
|
+
self._airbrussh_context = formatter.formatters.find do |fmt|
|
101
|
+
fmt.is_a?(Airbrussh::ConsoleFormatter)
|
102
|
+
end.context
|
97
103
|
self.enable_sudo_prompt = true
|
98
104
|
self.use_same_sudo_password = true
|
99
105
|
end
|
data/lib/runbook/entity.rb
CHANGED
@@ -7,11 +7,12 @@ module Runbook
|
|
7
7
|
child_class.const_set(:DSL, Runbook::DSL.class)
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
attr_reader :title, :dsl
|
10
|
+
attr_reader :title, :tags, :labels, :dsl
|
12
11
|
|
13
|
-
def initialize(title, parent: nil)
|
12
|
+
def initialize(title, tags: [], labels: {}, parent: nil)
|
14
13
|
@title = title
|
14
|
+
@tags = tags
|
15
|
+
@labels = labels
|
15
16
|
@parent = parent
|
16
17
|
@dsl = "#{self.class}::DSL".constantize.new(self)
|
17
18
|
end
|
@@ -25,7 +26,7 @@ module Runbook
|
|
25
26
|
@items ||= []
|
26
27
|
end
|
27
28
|
|
28
|
-
def method_missing(method, *args, &block)
|
29
|
+
ruby2_keywords def method_missing(method, *args, &block)
|
29
30
|
if dsl.respond_to?(method)
|
30
31
|
dsl.send(method, *args, &block)
|
31
32
|
else
|
@@ -92,7 +93,8 @@ module Runbook
|
|
92
93
|
|
93
94
|
def _run_metadata(items, item, metadata, index)
|
94
95
|
pos_index = items.select do |item|
|
95
|
-
item.is_a?(Entity)
|
96
|
+
item.is_a?(Entity) &&
|
97
|
+
!item.is_a?(Runbook::Entities::Setup)
|
96
98
|
end.index(item)
|
97
99
|
|
98
100
|
if pos_index
|
@@ -1,8 +1,12 @@
|
|
1
1
|
module Runbook::Extensions
|
2
2
|
module Sections
|
3
3
|
module DSL
|
4
|
-
def section(title, &block)
|
5
|
-
Runbook::Entities::Section.new(
|
4
|
+
def section(title, *tags, labels: {}, &block)
|
5
|
+
Runbook::Entities::Section.new(
|
6
|
+
title,
|
7
|
+
tags: tags,
|
8
|
+
labels: labels,
|
9
|
+
).tap do |section|
|
6
10
|
parent.add(section)
|
7
11
|
section.dsl.instance_eval(&block)
|
8
12
|
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
|
@@ -71,6 +71,8 @@ module Runbook::Extensions
|
|
71
71
|
Runbook::Entities::Step::DSL.prepend(SSHConfig::DSL)
|
72
72
|
Runbook::Entities::Section.prepend(SSHConfig)
|
73
73
|
Runbook::Entities::Section::DSL.prepend(SSHConfig::DSL)
|
74
|
+
Runbook::Entities::Setup.prepend(SSHConfig)
|
75
|
+
Runbook::Entities::Setup::DSL.prepend(SSHConfig::DSL)
|
74
76
|
Runbook::Entities::Book.prepend(SSHConfig)
|
75
77
|
Runbook::Entities::Book::DSL.prepend(SSHConfig::DSL)
|
76
78
|
end
|
@@ -1,10 +1,14 @@
|
|
1
1
|
module Runbook::Extensions
|
2
2
|
module Statements
|
3
3
|
module DSL
|
4
|
-
def method_missing(name, *args, &block)
|
4
|
+
ruby2_keywords def method_missing(name, *args, &block)
|
5
5
|
if (klass = Statements::DSL._statement_class(name))
|
6
6
|
klass.new(*args, &block).tap do |statement|
|
7
7
|
parent.add(statement)
|
8
|
+
|
9
|
+
if statement.respond_to?(:into)
|
10
|
+
Runbook.runtime_methods << statement.into
|
11
|
+
end
|
8
12
|
end
|
9
13
|
else
|
10
14
|
super
|
@@ -22,5 +26,6 @@ module Runbook::Extensions
|
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
29
|
+
Runbook::Entities::Setup::DSL.prepend(Statements::DSL)
|
25
30
|
Runbook::Entities::Step::DSL.prepend(Statements::DSL)
|
26
31
|
end
|
@@ -1,8 +1,17 @@
|
|
1
1
|
module Runbook::Extensions
|
2
2
|
module Steps
|
3
3
|
module DSL
|
4
|
-
def step(title=nil, &block)
|
5
|
-
|
4
|
+
def step(title=nil, *tags, labels: {}, &block)
|
5
|
+
if title.is_a?(Symbol)
|
6
|
+
tags.unshift(title)
|
7
|
+
title = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
Runbook::Entities::Step.new(
|
11
|
+
title,
|
12
|
+
tags: tags,
|
13
|
+
labels: labels,
|
14
|
+
).tap do |step|
|
6
15
|
parent.add(step)
|
7
16
|
step.dsl.instance_eval(&block) if block
|
8
17
|
end
|
@@ -10,5 +19,6 @@ module Runbook::Extensions
|
|
10
19
|
end
|
11
20
|
end
|
12
21
|
|
22
|
+
Runbook::Entities::Book::DSL.prepend(Steps::DSL)
|
13
23
|
Runbook::Entities::Section::DSL.prepend(Steps::DSL)
|
14
24
|
end
|
@@ -22,14 +22,23 @@ module Runbook::Generators
|
|
22
22
|
desc: "Target directory for shared runbook code"
|
23
23
|
class_option :test, type: :string, enum: ["rspec", "minitest"],
|
24
24
|
default: "rspec", desc: %Q{Test-suite, "rspec" or "minitest"}
|
25
|
+
class_option :ci, type: :string, enum: ["github", "travis", "gitlab", "circle"],
|
26
|
+
default: "github", desc: %Q{CI Service, "github", "travis", "gitlab", or "circle"}
|
25
27
|
|
26
28
|
def init_gem
|
27
29
|
bundle_exists = "which bundle 2>&1 1>/dev/null"
|
28
30
|
raise "Please ensure bundle is installed" unless system(bundle_exists)
|
31
|
+
bundler_version = Gem::Version.new(Bundler::VERSION)
|
29
32
|
|
30
33
|
inside(parent_options[:root]) do
|
31
34
|
test = "--test #{options[:test]}"
|
32
|
-
|
35
|
+
ci = "--ci #{options[:ci]}"
|
36
|
+
changelog = "--no-changelog" if bundler_version >= Gem::Version.new("2.2.8")
|
37
|
+
continue = (
|
38
|
+
run("bundle gem #{_name} #{test} #{ci} --rubocop #{changelog} --no-coc --no-mit") ||
|
39
|
+
options[:pretend]
|
40
|
+
)
|
41
|
+
exit 1 unless continue
|
33
42
|
end
|
34
43
|
end
|
35
44
|
|
@@ -49,6 +58,9 @@ module Runbook::Generators
|
|
49
58
|
remove_file(readme)
|
50
59
|
|
51
60
|
gemfile = File.join(*dirs, "Gemfile")
|
61
|
+
if File.exist?(gemfile)
|
62
|
+
@gemfile_file_contents = File.readlines(gemfile)
|
63
|
+
end
|
52
64
|
remove_file(gemfile)
|
53
65
|
|
54
66
|
base_file = File.join(*dirs, "lib", "#{_name}.rb")
|
@@ -98,14 +110,25 @@ module Runbook::Generators
|
|
98
110
|
template("templates/Gemfile.tt", target)
|
99
111
|
|
100
112
|
# Add development dependencies from gemspec
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
113
|
+
if @gemspec_file_contents
|
114
|
+
gems = @gemspec_file_contents.select do |line|
|
115
|
+
line =~ / spec.add_development_dependency/
|
116
|
+
end.map do |line|
|
117
|
+
line.gsub(/ spec.add_development_dependency/, "gem")
|
118
|
+
end.join
|
119
|
+
|
120
|
+
append_to_file(target, "\n#{gems}", verbose: false)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Add gemfile gems
|
124
|
+
if @gemfile_file_contents
|
125
|
+
gems = @gemfile_file_contents.select do |line|
|
126
|
+
line =~ /^gem /
|
127
|
+
end.join
|
128
|
+
|
129
|
+
append_to_file(target, "\n#{gems}", verbose: false)
|
130
|
+
end
|
131
|
+
|
109
132
|
end
|
110
133
|
|
111
134
|
def create_base_file
|