runbook 0.14.0 → 1.1.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.
- 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
@@ -1,9 +1,11 @@
|
|
1
1
|
module Runbook::Helpers
|
2
2
|
module TmuxHelper
|
3
|
+
FILE_PERMISSIONS = 0600
|
4
|
+
|
3
5
|
def setup_layout(structure, runbook_title:)
|
4
6
|
_remove_stale_layouts
|
5
7
|
layout_file = _layout_file(_slug(runbook_title))
|
6
|
-
if File.
|
8
|
+
if File.exist?(layout_file)
|
7
9
|
stored_layout = ::YAML::load_file(layout_file)
|
8
10
|
if _all_panes_exist?(stored_layout)
|
9
11
|
return stored_layout
|
@@ -11,14 +13,14 @@ module Runbook::Helpers
|
|
11
13
|
end
|
12
14
|
|
13
15
|
_setup_layout(structure).tap do |layout_panes|
|
14
|
-
File.open(layout_file, 'w') do |f|
|
16
|
+
File.open(layout_file, 'w', FILE_PERMISSIONS) do |f|
|
15
17
|
f.write(layout_panes.to_yaml)
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
22
|
def send_keys(command, target)
|
21
|
-
`tmux send-keys -t #{target} #{_pager_escape_sequence}
|
23
|
+
`tmux send-keys -t #{target} #{_pager_escape_sequence} #{Shellwords.escape(command)} C-m`
|
22
24
|
end
|
23
25
|
|
24
26
|
def kill_all_panes(layout_panes)
|
@@ -41,7 +43,7 @@ module Runbook::Helpers
|
|
41
43
|
end
|
42
44
|
|
43
45
|
def _slug(title)
|
44
|
-
title.titleize.gsub(
|
46
|
+
title.titleize.gsub(/[\/\s]+/, "").underscore.dasherize
|
45
47
|
end
|
46
48
|
|
47
49
|
def _all_panes_exist?(stored_layout)
|
data/lib/runbook/node.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Runbook
|
2
2
|
class Node
|
3
|
+
attr_accessor :parent
|
4
|
+
|
3
5
|
def initialize
|
4
6
|
raise "Should not be initialized"
|
5
7
|
end
|
@@ -19,5 +21,13 @@ module Runbook
|
|
19
21
|
def visited?
|
20
22
|
@visited
|
21
23
|
end
|
24
|
+
|
25
|
+
def parent_entity
|
26
|
+
node = self
|
27
|
+
while(node && !node.is_a?(Runbook::Entity))
|
28
|
+
node = node.parent
|
29
|
+
end
|
30
|
+
node
|
31
|
+
end
|
22
32
|
end
|
23
33
|
end
|
data/lib/runbook/run.rb
CHANGED
@@ -36,6 +36,10 @@ module Runbook
|
|
36
36
|
metadata[:toolbox].output("Section #{metadata[:position]}: #{object.title}\n\n")
|
37
37
|
end
|
38
38
|
|
39
|
+
def runbook__entities__setup(object, metadata)
|
40
|
+
metadata[:toolbox].output("Setup:\n\n")
|
41
|
+
end
|
42
|
+
|
39
43
|
def runbook__entities__step(object, metadata)
|
40
44
|
toolbox = metadata[:toolbox]
|
41
45
|
title = " #{object.title}".rstrip
|
@@ -66,11 +70,12 @@ module Runbook
|
|
66
70
|
|
67
71
|
if metadata[:noop]
|
68
72
|
default_msg = default ? " (default: #{default})" : ""
|
69
|
-
|
73
|
+
echo_msg = object.echo ? "" : " (echo: false)"
|
74
|
+
metadata[:toolbox].output("[NOOP] Ask: #{object.prompt} (store in: #{object.into})#{default_msg}#{echo_msg}")
|
70
75
|
return
|
71
76
|
end
|
72
77
|
|
73
|
-
result = metadata[:toolbox].ask(object.prompt, default: default)
|
78
|
+
result = metadata[:toolbox].ask(object.prompt, default: default, echo: object.echo)
|
74
79
|
|
75
80
|
target = object.parent.dsl
|
76
81
|
target.singleton_class.class_eval { attr_accessor object.into }
|
@@ -152,7 +157,7 @@ module Runbook
|
|
152
157
|
next_index = metadata[:index] + 1
|
153
158
|
parent_items = object.parent.items
|
154
159
|
remaining_items = parent_items.slice!(next_index..-1)
|
155
|
-
object.parent.dsl.instance_exec(object, metadata, &object.block)
|
160
|
+
object.parent.dsl.instance_exec(object, metadata, self, &object.block)
|
156
161
|
parent_items[next_index..-1].each { |item| item.dynamic! }
|
157
162
|
parent_items.push(*remaining_items)
|
158
163
|
end
|
@@ -228,9 +233,9 @@ module Runbook
|
|
228
233
|
return
|
229
234
|
when :skip
|
230
235
|
position = metadata[:position]
|
231
|
-
|
232
|
-
new_step =
|
233
|
-
start_at =
|
236
|
+
split_position = position.split(".")
|
237
|
+
new_step = (split_position.pop.to_i + 1).to_s
|
238
|
+
start_at = (split_position << new_step).join(".")
|
234
239
|
metadata[:start_at] = start_at
|
235
240
|
when :jump
|
236
241
|
result = toolbox.ask("What position would you like to jump to?")
|
@@ -253,7 +258,7 @@ module Runbook
|
|
253
258
|
:after,
|
254
259
|
Runbook::Entities::Book,
|
255
260
|
) do |object, metadata|
|
256
|
-
next if metadata[:noop] || metadata[:layout_panes].none?
|
261
|
+
next if metadata[:noop] || metadata[:layout_panes].none? || metadata[:keep_panes]
|
257
262
|
if metadata[:auto]
|
258
263
|
metadata[:toolbox].output("Killing all opened tmux panes...")
|
259
264
|
kill_all_panes(metadata[:layout_panes])
|
@@ -273,7 +278,8 @@ module Runbook
|
|
273
278
|
:after,
|
274
279
|
Runbook::Statement,
|
275
280
|
) do |object, metadata|
|
276
|
-
if object.parent.is_a?(Runbook::Entities::Step)
|
281
|
+
if object.parent.is_a?(Runbook::Entities::Step) ||
|
282
|
+
object.parent.is_a?(Runbook::Entities::Setup)
|
277
283
|
if object.parent.items.last == object
|
278
284
|
metadata[:toolbox].output("\n")
|
279
285
|
end
|
data/lib/runbook/runner.rb
CHANGED
@@ -11,6 +11,7 @@ module Runbook
|
|
11
11
|
noop: false,
|
12
12
|
auto: false,
|
13
13
|
paranoid: true,
|
14
|
+
keep_panes: false,
|
14
15
|
start_at: "0"
|
15
16
|
)
|
16
17
|
run = "Runbook::Runs::#{run.to_s.camelize}".constantize
|
@@ -21,6 +22,7 @@ module Runbook
|
|
21
22
|
paranoid: Util::Glue.new(paranoid),
|
22
23
|
start_at: Util::Glue.new(start_at || "0"),
|
23
24
|
toolbox: Util::Glue.new(toolbox),
|
25
|
+
keep_panes: keep_panes,
|
24
26
|
book_title: book.title,
|
25
27
|
}).
|
26
28
|
merge(Runbook::Entities::Book.initial_run_metadata).
|
data/lib/runbook/runs/ssh_kit.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
module Runbook::Runs
|
2
2
|
module SSHKit
|
3
3
|
include Runbook::Run
|
4
|
-
extend Runbook::Helpers::SSHKitHelper
|
5
4
|
|
6
5
|
def self.included(base)
|
7
6
|
base.extend(ClassMethods)
|
8
7
|
end
|
9
8
|
|
10
9
|
module ClassMethods
|
10
|
+
include Runbook::Helpers::SSHKitHelper
|
11
|
+
|
12
|
+
def runbook__entities__step(object, metadata)
|
13
|
+
airbrussh_context = Runbook.configuration._airbrussh_context
|
14
|
+
airbrussh_context.set_current_task_name(object.title)
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
11
18
|
def runbook__statements__assert(object, metadata)
|
12
19
|
cmd_ssh_config = find_ssh_config(object, :cmd_ssh_config)
|
13
20
|
|
@@ -19,18 +26,18 @@ module Runbook::Runs
|
|
19
26
|
if object.timeout > 0 || object.attempts > 0
|
20
27
|
timeout_msg = object.timeout > 0 ? "#{object.timeout} second(s)" : nil
|
21
28
|
attempts_msg = object.attempts > 0 ? "#{object.attempts} attempts" : nil
|
22
|
-
|
23
|
-
metadata[:toolbox].output(
|
24
|
-
if object.
|
25
|
-
object.
|
26
|
-
object.
|
29
|
+
abort_msg = "after #{[timeout_msg, attempts_msg].compact.join(" or ")}, abort..."
|
30
|
+
metadata[:toolbox].output(abort_msg)
|
31
|
+
if object.abort_statement
|
32
|
+
object.abort_statement.parent = object.parent
|
33
|
+
object.abort_statement.run(self, metadata.dup)
|
27
34
|
end
|
28
35
|
metadata[:toolbox].output("and exit")
|
29
36
|
end
|
30
37
|
return
|
31
38
|
end
|
32
39
|
|
33
|
-
|
40
|
+
should_abort = false
|
34
41
|
test_args = ssh_kit_command(object.cmd, raw: object.cmd_raw)
|
35
42
|
test_options = ssh_kit_command_options(cmd_ssh_config)
|
36
43
|
|
@@ -39,12 +46,12 @@ module Runbook::Runs
|
|
39
46
|
count = object.attempts
|
40
47
|
while !(test(*test_args, test_options))
|
41
48
|
if ((count -= 1) == 0)
|
42
|
-
|
49
|
+
should_abort = true
|
43
50
|
break
|
44
51
|
end
|
45
52
|
|
46
53
|
if (object.timeout > 0 && Time.now - time > object.timeout)
|
47
|
-
|
54
|
+
should_abort = true
|
48
55
|
break
|
49
56
|
end
|
50
57
|
|
@@ -52,12 +59,12 @@ module Runbook::Runs
|
|
52
59
|
end
|
53
60
|
end
|
54
61
|
|
55
|
-
if
|
62
|
+
if should_abort
|
56
63
|
error_msg = "Error! Assertion `#{object.cmd}` failed"
|
57
64
|
metadata[:toolbox].error(error_msg)
|
58
|
-
if object.
|
59
|
-
object.
|
60
|
-
object.
|
65
|
+
if object.abort_statement
|
66
|
+
object.abort_statement.parent = object.parent
|
67
|
+
object.abort_statement.run(self, metadata.dup)
|
61
68
|
end
|
62
69
|
raise Runbook::Runner::ExecutionError, error_msg
|
63
70
|
end
|
data/lib/runbook/statement.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module Runbook::Statements
|
2
2
|
class Ask < Runbook::Statement
|
3
|
-
attr_reader :prompt, :into, :default
|
3
|
+
attr_reader :prompt, :into, :default, :echo
|
4
4
|
|
5
|
-
def initialize(prompt, into:, default: nil)
|
5
|
+
def initialize(prompt, into:, default: nil, echo: true)
|
6
6
|
@prompt = prompt
|
7
7
|
@into = into
|
8
8
|
@default = default
|
9
|
+
@echo = echo
|
9
10
|
end
|
10
11
|
end
|
11
12
|
end
|
@@ -2,7 +2,12 @@ module Runbook::Statements
|
|
2
2
|
class Assert < Runbook::Statement
|
3
3
|
attr_reader :cmd, :cmd_ssh_config, :cmd_raw
|
4
4
|
attr_reader :interval, :timeout, :attempts
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :abort_statement
|
6
|
+
|
7
|
+
def timeout_statement
|
8
|
+
Runbook.deprecator.deprecation_warning(:timeout_statement, :abort_statement)
|
9
|
+
@abort_statement
|
10
|
+
end
|
6
11
|
|
7
12
|
def initialize(
|
8
13
|
cmd,
|
@@ -11,6 +16,7 @@ module Runbook::Statements
|
|
11
16
|
interval: 1,
|
12
17
|
timeout: 0,
|
13
18
|
attempts: 0,
|
19
|
+
abort_statement: nil,
|
14
20
|
timeout_statement: nil
|
15
21
|
)
|
16
22
|
@cmd = cmd
|
@@ -19,7 +25,10 @@ module Runbook::Statements
|
|
19
25
|
@interval = interval
|
20
26
|
@timeout = timeout
|
21
27
|
@attempts = attempts
|
22
|
-
|
28
|
+
if timeout_statement
|
29
|
+
Runbook.deprecator.deprecation_warning(:timeout_statement, :abort_statement)
|
30
|
+
end
|
31
|
+
@abort_statement = abort_statement || timeout_statement
|
23
32
|
end
|
24
33
|
end
|
25
34
|
end
|
data/lib/runbook/toolbox.rb
CHANGED
@@ -6,8 +6,8 @@ module Runbook
|
|
6
6
|
@prompt = TTY::Prompt.new
|
7
7
|
end
|
8
8
|
|
9
|
-
def ask(msg, default: nil)
|
10
|
-
prompt.ask(msg, default: default)
|
9
|
+
def ask(msg, default: nil, echo: true)
|
10
|
+
prompt.ask(msg, default: default, echo: echo)
|
11
11
|
end
|
12
12
|
|
13
13
|
def expand(msg, choices)
|
@@ -15,13 +15,7 @@ module Runbook
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def yes?(msg)
|
18
|
-
|
19
|
-
prompt.yes?(msg)
|
20
|
-
rescue TTY::Prompt::ConversionError
|
21
|
-
err_msg = "Unknown input: Please type 'y' or 'n'."
|
22
|
-
warn(err_msg)
|
23
|
-
retry
|
24
|
-
end
|
18
|
+
prompt.yes?(msg)
|
25
19
|
end
|
26
20
|
|
27
21
|
def output(msg)
|
data/lib/runbook/util/repo.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module Runbook::Util
|
2
2
|
module Repo
|
3
3
|
FILE_ID = "data"
|
4
|
+
FILE_PERMISSIONS = 0600
|
4
5
|
|
5
6
|
def self.load(metadata)
|
6
7
|
title = metadata[:book_title]
|
7
8
|
file = _file(title)
|
8
|
-
if File.
|
9
|
+
if File.exist?(file)
|
9
10
|
msg = "Repo file #{file} detected. Loading previous state..."
|
10
11
|
metadata[:toolbox].output(msg)
|
11
12
|
metadata[:repo] = ::YAML::load_file(file)
|
@@ -13,7 +14,7 @@ module Runbook::Util
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def self.save(repo, book_title:)
|
16
|
-
File.open(_file(book_title), 'w') do |f|
|
17
|
+
File.open(_file(book_title), 'w', FILE_PERMISSIONS) do |f|
|
17
18
|
f.write(repo.to_yaml)
|
18
19
|
end
|
19
20
|
end
|
@@ -27,7 +28,7 @@ module Runbook::Util
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def self._slug(title)
|
30
|
-
title.titleize.gsub(
|
31
|
+
title.titleize.gsub(/[\/\s]+/, "").underscore.dasherize
|
31
32
|
end
|
32
33
|
|
33
34
|
def self.register_save_repo_hook(base)
|
data/lib/runbook/util/runbook.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
module Runbook::Util
|
2
2
|
module StoredPose
|
3
3
|
FILE_ID = "current_position"
|
4
|
+
FILE_PERMISSIONS = 0600
|
4
5
|
|
5
6
|
def self.load(metadata)
|
6
7
|
title = metadata[:book_title]
|
7
8
|
file = _file(title)
|
8
|
-
if File.
|
9
|
+
if File.exist?(file)
|
9
10
|
::YAML::load_file(file)
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
13
14
|
def self.save(repo, book_title:)
|
14
|
-
File.open(_file(book_title), 'w') do |f|
|
15
|
+
File.open(_file(book_title), 'w', FILE_PERMISSIONS) do |f|
|
15
16
|
f.write(repo.to_yaml)
|
16
17
|
end
|
17
18
|
end
|
@@ -25,7 +26,7 @@ module Runbook::Util
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def self._slug(title)
|
28
|
-
title.titleize.gsub(
|
29
|
+
title.titleize.gsub(/[\/\s]+/, "").underscore.dasherize
|
29
30
|
end
|
30
31
|
|
31
32
|
def self.register_save_pose_hook(base)
|
data/lib/runbook/version.rb
CHANGED
@@ -13,6 +13,14 @@ module Runbook::Views
|
|
13
13
|
output << "#{heading} #{metadata[:index]+1}. #{object.title}\n\n"
|
14
14
|
end
|
15
15
|
|
16
|
+
def self.runbook__entities__setup(object, output, metadata)
|
17
|
+
output << "[] #{object.title}\n\n"
|
18
|
+
|
19
|
+
ssh_config = find_ssh_config(object)
|
20
|
+
ssh_config_output = render_ssh_config_output(ssh_config)
|
21
|
+
output << "#{ssh_config_output}\n" unless ssh_config_output.empty?
|
22
|
+
end
|
23
|
+
|
16
24
|
def self.runbook__entities__step(object, output, metadata)
|
17
25
|
output << "#{metadata[:index]+1}. [] #{object.title}\n\n"
|
18
26
|
|
@@ -23,7 +31,7 @@ module Runbook::Views
|
|
23
31
|
|
24
32
|
def self.runbook__statements__ask(object, output, metadata)
|
25
33
|
default_msg = object.default ? " (default: #{object.default})" : ""
|
26
|
-
output << " #{object.prompt} into
|
34
|
+
output << " #{object.prompt} into `#{object.into}`#{default_msg}\n\n"
|
27
35
|
end
|
28
36
|
|
29
37
|
def self.runbook__statements__assert(object, output, metadata)
|
@@ -31,21 +39,21 @@ module Runbook::Views
|
|
31
39
|
if object.timeout > 0 || object.attempts > 0
|
32
40
|
timeout_msg = object.timeout > 0 ? "#{object.timeout} second(s)" : nil
|
33
41
|
attempts_msg = object.attempts > 0 ? "#{object.attempts} attempts" : nil
|
34
|
-
|
35
|
-
output << " #{
|
36
|
-
if object.
|
37
|
-
object.
|
42
|
+
abort_msg = "after #{[timeout_msg, attempts_msg].compact.join(" or ")}, abort..."
|
43
|
+
output << " #{abort_msg}\n\n"
|
44
|
+
if object.abort_statement
|
45
|
+
object.abort_statement.render(self, output, metadata.dup)
|
38
46
|
end
|
39
47
|
output << " and exit\n\n"
|
40
48
|
end
|
41
49
|
end
|
42
50
|
|
43
51
|
def self.runbook__statements__capture(object, output, metadata)
|
44
|
-
output << " capture: `#{object.cmd}` into
|
52
|
+
output << " capture: `#{object.cmd}` into `#{object.into}`\n\n"
|
45
53
|
end
|
46
54
|
|
47
55
|
def self.runbook__statements__capture_all(object, output, metadata)
|
48
|
-
output << " capture_all: `#{object.cmd}` into
|
56
|
+
output << " capture_all: `#{object.cmd}` into `#{object.into}`\n\n"
|
49
57
|
end
|
50
58
|
|
51
59
|
def self.runbook__statements__command(object, output, metadata)
|
data/runbook.gemspec
CHANGED
@@ -31,19 +31,23 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
32
32
|
spec.require_paths = ["lib"]
|
33
33
|
|
34
|
-
spec.add_runtime_dependency
|
35
|
-
spec.add_runtime_dependency "method_source", "~> 0
|
36
|
-
spec.add_runtime_dependency "
|
34
|
+
spec.add_runtime_dependency "activesupport", ">= 5.0.1.x", "< 7.0"
|
35
|
+
spec.add_runtime_dependency "method_source", "~> 1.0"
|
36
|
+
spec.add_runtime_dependency "ruby2_keywords", "~> 0.0.4"
|
37
|
+
spec.add_runtime_dependency "sshkit", "1.21.0"
|
37
38
|
spec.add_runtime_dependency "sshkit-sudo", "~> 0.1"
|
38
|
-
spec.add_runtime_dependency "airbrussh", "~> 1.
|
39
|
+
spec.add_runtime_dependency "airbrussh", "~> 1.4"
|
39
40
|
spec.add_runtime_dependency "thor", "~> 0.20"
|
40
41
|
spec.add_runtime_dependency "tty-progressbar", "~> 0.14"
|
41
|
-
spec.add_runtime_dependency "tty-prompt", "~> 0.
|
42
|
+
spec.add_runtime_dependency "tty-prompt", "~> 0.20"
|
42
43
|
|
44
|
+
spec.add_development_dependency "appraisal", "~> 2.2"
|
43
45
|
spec.add_development_dependency "aruba", "~> 0.14"
|
44
|
-
spec.add_development_dependency "bundler", "~>
|
45
|
-
spec.add_development_dependency "
|
46
|
+
spec.add_development_dependency "bundler", "~> 2.2"
|
47
|
+
spec.add_development_dependency "bcrypt_pbkdf", ">= 1.0", "< 2.0"
|
48
|
+
spec.add_development_dependency "ed25519", ">= 1.2", "< 2.0"
|
49
|
+
spec.add_development_dependency "pry", "~> 0.13"
|
46
50
|
spec.add_development_dependency "pry-byebug", "~> 3.6"
|
47
|
-
spec.add_development_dependency "rake", "~>
|
51
|
+
spec.add_development_dependency "rake", "~> 12.3"
|
48
52
|
spec.add_development_dependency "rspec", "~> 3.0"
|
49
53
|
end
|