dk 0.0.1 → 0.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 +5 -5
- data/README.md +643 -1
- data/bin/dk +7 -0
- data/dk.gemspec +7 -3
- data/lib/dk/ansi.rb +98 -0
- data/lib/dk/cli.rb +173 -0
- data/lib/dk/config.rb +217 -0
- data/lib/dk/config_runner.rb +24 -0
- data/lib/dk/dk_runner.rb +13 -0
- data/lib/dk/dry_runner.rb +43 -0
- data/lib/dk/has_set_param.rb +42 -0
- data/lib/dk/has_ssh_opts.rb +36 -0
- data/lib/dk/has_the_runs.rb +23 -0
- data/lib/dk/has_the_stubs.rb +116 -0
- data/lib/dk/local.rb +84 -0
- data/lib/dk/null_logger.rb +13 -0
- data/lib/dk/remote.rb +132 -0
- data/lib/dk/runner.rb +202 -0
- data/lib/dk/task.rb +266 -0
- data/lib/dk/task_run.rb +17 -0
- data/lib/dk/test_runner.rb +54 -0
- data/lib/dk/tree_runner.rb +64 -0
- data/lib/dk/version.rb +1 -1
- data/lib/dk.rb +23 -1
- data/test/helper.rb +6 -1
- data/test/support/config/dk.rb +7 -0
- data/test/support/config/task_defs.rb +10 -0
- data/test/support/factory.rb +38 -0
- data/test/support/log/.gitkeep +0 -0
- data/test/system/has_the_stubs_tests.rb +355 -0
- data/test/system/runner_tests.rb +222 -0
- data/test/unit/ansi_tests.rb +40 -0
- data/test/unit/cli_tests.rb +317 -0
- data/test/unit/config_runner_tests.rb +60 -0
- data/test/unit/config_tests.rb +427 -0
- data/test/unit/dk_runner_tests.rb +34 -0
- data/test/unit/dk_tests.rb +49 -0
- data/test/unit/dry_runner_tests.rb +71 -0
- data/test/unit/has_set_param_tests.rb +46 -0
- data/test/unit/has_ssh_opts_tests.rb +81 -0
- data/test/unit/has_the_runs_tests.rb +37 -0
- data/test/unit/has_the_stubs_tests.rb +279 -0
- data/test/unit/local_tests.rb +174 -0
- data/test/unit/null_logger_tests.rb +17 -0
- data/test/unit/remote_tests.rb +330 -0
- data/test/unit/runner_tests.rb +398 -0
- data/test/unit/task_run_tests.rb +40 -0
- data/test/unit/task_tests.rb +943 -0
- data/test/unit/test_runner_tests.rb +189 -0
- data/test/unit/tree_runner_tests.rb +152 -0
- metadata +106 -9
data/lib/dk/task.rb
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'much-plugin'
|
2
|
+
require 'dk'
|
3
|
+
require 'dk/remote'
|
4
|
+
require 'dk/runner'
|
5
|
+
|
6
|
+
module Dk
|
7
|
+
|
8
|
+
module Task
|
9
|
+
include MuchPlugin
|
10
|
+
|
11
|
+
class ParamsHash < Hash
|
12
|
+
def try_param(key)
|
13
|
+
begin; self.[](key); rescue Dk::NoParamError; nil; end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
plugin_included do
|
18
|
+
include InstanceMethods
|
19
|
+
extend ClassMethods
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
|
25
|
+
def initialize(runner, params = nil)
|
26
|
+
params ||= {}
|
27
|
+
@dk_runner = runner
|
28
|
+
@dk_params = ParamsHash.new{ |h, k| @dk_runner.params[k] }
|
29
|
+
@dk_params.merge!(params)
|
30
|
+
end
|
31
|
+
|
32
|
+
def dk_run
|
33
|
+
self.dk_run_callbacks 'before'
|
34
|
+
@dk_runner.log_task_run(self.class) do
|
35
|
+
catch(:halt){ self.run! }
|
36
|
+
end
|
37
|
+
self.dk_run_callbacks 'after'
|
38
|
+
end
|
39
|
+
|
40
|
+
def run!
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
|
44
|
+
def dk_run_callbacks(named)
|
45
|
+
callbacks = CallbackSet.new
|
46
|
+
callbacks.add_callbacks(@dk_runner.task_callbacks("prepend_#{named}", self.class))
|
47
|
+
callbacks.add_callbacks(self.class.send("#{named}_callbacks") || [])
|
48
|
+
callbacks.add_callbacks(@dk_runner.task_callbacks(named, self.class))
|
49
|
+
callbacks.each{ |c| run_task(c.task_class, c.params) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def dk_dsl_ssh_hosts
|
53
|
+
@dk_dsl_ssh_hosts ||= self.instance_eval(&self.class.ssh_hosts)
|
54
|
+
end
|
55
|
+
|
56
|
+
def ==(other_task)
|
57
|
+
self.class == other_task.class
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# Helpers
|
63
|
+
|
64
|
+
def run_task(task_class, params = nil)
|
65
|
+
@dk_runner.run_task(task_class, params)
|
66
|
+
end
|
67
|
+
|
68
|
+
def cmd(cmd_str, *args)
|
69
|
+
given_opts = args.last.kind_of?(::Hash) ? args.pop : nil
|
70
|
+
input = args.last
|
71
|
+
@dk_runner.cmd(self, cmd_str, input, given_opts)
|
72
|
+
end
|
73
|
+
|
74
|
+
def cmd!(cmd_str, *args)
|
75
|
+
cmd = cmd(cmd_str, *args)
|
76
|
+
if !cmd.success?
|
77
|
+
raise CmdRunError, "error running `#{cmd.cmd_str}`", caller
|
78
|
+
end
|
79
|
+
cmd
|
80
|
+
end
|
81
|
+
|
82
|
+
def ssh(cmd_str, *args)
|
83
|
+
given_opts = args.last.kind_of?(::Hash) ? args.pop : nil
|
84
|
+
input = args.last
|
85
|
+
@dk_runner.ssh(self, cmd_str, input, given_opts, dk_build_ssh_opts(given_opts))
|
86
|
+
end
|
87
|
+
|
88
|
+
def ssh!(cmd_str, *args)
|
89
|
+
cmd = ssh(cmd_str, *args)
|
90
|
+
if !cmd.success?
|
91
|
+
raise SSHRunError, "error running `#{cmd.cmd_str}` over ssh", caller
|
92
|
+
end
|
93
|
+
cmd
|
94
|
+
end
|
95
|
+
|
96
|
+
def params
|
97
|
+
@dk_params
|
98
|
+
end
|
99
|
+
|
100
|
+
def set_param(key, value)
|
101
|
+
@dk_runner.set_param(key, value)
|
102
|
+
end
|
103
|
+
|
104
|
+
def param?(key)
|
105
|
+
@dk_params.key?(key) || @dk_runner.params.key?(key)
|
106
|
+
end
|
107
|
+
|
108
|
+
def try_param(key)
|
109
|
+
@dk_params.try_param(key)
|
110
|
+
end
|
111
|
+
|
112
|
+
def before(subject, callback, params = nil)
|
113
|
+
@dk_runner.add_task_callback('before', subject, callback, params)
|
114
|
+
end
|
115
|
+
|
116
|
+
def prepend_before(subject, callback, params = nil)
|
117
|
+
@dk_runner.add_task_callback('prepend_before', subject, callback, params)
|
118
|
+
end
|
119
|
+
|
120
|
+
def after(subject, callback, params = nil)
|
121
|
+
@dk_runner.add_task_callback('after', subject, callback, params)
|
122
|
+
end
|
123
|
+
|
124
|
+
def prepend_after(subject, callback, params = nil)
|
125
|
+
@dk_runner.add_task_callback('prepend_after', subject, callback, params)
|
126
|
+
end
|
127
|
+
|
128
|
+
def ssh_hosts(group_name = nil, *values)
|
129
|
+
@dk_runner.ssh_hosts(group_name, *values)
|
130
|
+
end
|
131
|
+
|
132
|
+
def ssh_cmd_str(cmd_str, opts = nil)
|
133
|
+
opts ||= {}
|
134
|
+
Remote.ssh_cmd_str(
|
135
|
+
cmd_str,
|
136
|
+
opts[:host].to_s,
|
137
|
+
dk_lookup_ssh_args(opts[:ssh_args]),
|
138
|
+
dk_lookup_host_ssh_args(opts[:host_ssh_args])
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
def halt
|
143
|
+
throw :halt
|
144
|
+
end
|
145
|
+
|
146
|
+
def log_info(msg, *styles); @dk_runner.log_info(msg, *styles); end
|
147
|
+
def log_debug(msg, *styles); @dk_runner.log_debug(msg, *styles); end
|
148
|
+
def log_error(msg, *styles); @dk_runner.log_error(msg, *styles); end
|
149
|
+
|
150
|
+
def dk_build_ssh_opts(opts)
|
151
|
+
opts ||= {}
|
152
|
+
opts.merge({
|
153
|
+
:hosts => dk_lookup_ssh_hosts(opts[:hosts]),
|
154
|
+
:ssh_args => dk_lookup_ssh_args(opts[:ssh_args]),
|
155
|
+
:host_ssh_args => dk_lookup_host_ssh_args(opts[:host_ssh_args])
|
156
|
+
})
|
157
|
+
end
|
158
|
+
|
159
|
+
def dk_lookup_ssh_hosts(opts_hosts)
|
160
|
+
[*(
|
161
|
+
ssh_hosts[opts_hosts] ||
|
162
|
+
opts_hosts ||
|
163
|
+
ssh_hosts[self.dk_dsl_ssh_hosts] ||
|
164
|
+
self.dk_dsl_ssh_hosts
|
165
|
+
)]
|
166
|
+
end
|
167
|
+
|
168
|
+
def dk_lookup_ssh_args(opts_args)
|
169
|
+
opts_args || @dk_runner.ssh_args
|
170
|
+
end
|
171
|
+
|
172
|
+
def dk_lookup_host_ssh_args(opts_args)
|
173
|
+
opts_args || @dk_runner.host_ssh_args
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
CmdRunError = Class.new(RuntimeError)
|
179
|
+
SSHRunError = Class.new(RuntimeError)
|
180
|
+
|
181
|
+
module ClassMethods
|
182
|
+
|
183
|
+
def description(value = nil)
|
184
|
+
@description = value.to_s if !value.nil?
|
185
|
+
@description
|
186
|
+
end
|
187
|
+
alias_method :desc, :description
|
188
|
+
|
189
|
+
def before_callbacks; @before_callbacks ||= CallbackSet.new; end
|
190
|
+
def after_callbacks; @after_callbacks ||= CallbackSet.new; end
|
191
|
+
|
192
|
+
def before_callback_task_classes; self.before_callbacks.map(&:task_class); end
|
193
|
+
def after_callback_task_classes; self.after_callbacks.map(&:task_class); end
|
194
|
+
|
195
|
+
def before(task_class, params = nil)
|
196
|
+
self.before_callbacks << Callback.new(task_class, params)
|
197
|
+
end
|
198
|
+
|
199
|
+
def after(task_class, params = nil)
|
200
|
+
self.after_callbacks << Callback.new(task_class, params)
|
201
|
+
end
|
202
|
+
|
203
|
+
def prepend_before(task_class, params = nil)
|
204
|
+
self.before_callbacks.unshift(Callback.new(task_class, params))
|
205
|
+
end
|
206
|
+
|
207
|
+
def prepend_after(task_class, params = nil)
|
208
|
+
self.after_callbacks.unshift(Callback.new(task_class, params))
|
209
|
+
end
|
210
|
+
|
211
|
+
def ssh_hosts(value = nil, &block)
|
212
|
+
@ssh_hosts = block || proc{ value } if !block.nil? || !value.nil?
|
213
|
+
@ssh_hosts || proc{}
|
214
|
+
end
|
215
|
+
|
216
|
+
def run_only_once(value = nil)
|
217
|
+
@run_only_once = !!value if !value.nil?
|
218
|
+
@run_only_once
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
Callback = Struct.new(:task_class, :params)
|
224
|
+
|
225
|
+
class CallbackSet < ::Array
|
226
|
+
|
227
|
+
def <<(callback); super unless self.include?(callback); end
|
228
|
+
def unshift(callback); super unless self.include?(callback); end
|
229
|
+
|
230
|
+
def add_callbacks(callbacks)
|
231
|
+
callbacks.each{ |cb| self.<<(cb) }
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
module TestHelpers
|
237
|
+
include MuchPlugin
|
238
|
+
|
239
|
+
plugin_included do
|
240
|
+
require 'dk/test_runner'
|
241
|
+
include InstanceMethods
|
242
|
+
end
|
243
|
+
|
244
|
+
module InstanceMethods
|
245
|
+
|
246
|
+
def test_runner(task_class, args = nil)
|
247
|
+
Dk::TestRunner.new(args).tap do |runner|
|
248
|
+
runner.task_class = task_class
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_task(task_class, args = nil)
|
253
|
+
test_runner(task_class, args).task
|
254
|
+
end
|
255
|
+
|
256
|
+
def ssh_cmd_str(task, *args)
|
257
|
+
task.instance_eval{ ssh_cmd_str(*args) }
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
data/lib/dk/task_run.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'dk/has_the_runs'
|
2
|
+
require 'dk/has_the_stubs'
|
3
|
+
require 'dk/local'
|
4
|
+
require 'dk/remote'
|
5
|
+
require 'dk/runner'
|
6
|
+
require 'dk/task_run'
|
7
|
+
|
8
|
+
module Dk
|
9
|
+
|
10
|
+
class TestRunner < Runner
|
11
|
+
include HasTheRuns
|
12
|
+
include HasTheStubs
|
13
|
+
|
14
|
+
attr_accessor :task_class
|
15
|
+
|
16
|
+
# test runners are designed to only run their task
|
17
|
+
def run(params = nil)
|
18
|
+
self.task(params).tap(&:dk_run)
|
19
|
+
end
|
20
|
+
|
21
|
+
# don't run any sub-tasks, just track that a sub-task was run
|
22
|
+
def run_task(task_class, params = nil)
|
23
|
+
TaskRun.new(task_class, params).tap{ |tr| self.runs << tr }
|
24
|
+
end
|
25
|
+
|
26
|
+
# track that a local cmd was run
|
27
|
+
def cmd(task, cmd_str, input, given_opts)
|
28
|
+
super(task, cmd_str, input, given_opts).tap{ |c| self.runs << c }
|
29
|
+
end
|
30
|
+
|
31
|
+
# track that a remote cmd was run
|
32
|
+
def ssh(task, cmd_str, input, given_opts, ssh_opts)
|
33
|
+
super(task, cmd_str, input, given_opts, ssh_opts).tap{ |c| self.runs << c }
|
34
|
+
end
|
35
|
+
|
36
|
+
# test task API
|
37
|
+
|
38
|
+
def task(params = nil)
|
39
|
+
@task ||= build_task(self.task_class, params)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def has_the_stubs_build_local_cmd(cmd_str, given_opts)
|
45
|
+
Local::CmdSpy.new(cmd_str, given_opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_the_stubs_build_remote_cmd(cmd_str, ssh_opts)
|
49
|
+
Remote::CmdSpy.new(cmd_str, ssh_opts)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'dk/dry_runner'
|
2
|
+
require 'dk/has_the_runs'
|
3
|
+
require 'dk/null_logger'
|
4
|
+
require 'dk/task_run'
|
5
|
+
|
6
|
+
module Dk
|
7
|
+
|
8
|
+
class TreeRunner < DryRunner
|
9
|
+
include HasTheRuns
|
10
|
+
|
11
|
+
LEVEL_PREFIX = ' '.freeze
|
12
|
+
LEVEL_BULLET = '|-- '.freeze
|
13
|
+
|
14
|
+
def initialize(config, kernel)
|
15
|
+
super(config, :logger => NullLogger.new) # disable any logging
|
16
|
+
|
17
|
+
@task_run_stack = [self]
|
18
|
+
@run_num = 0
|
19
|
+
@kernel = kernel
|
20
|
+
end
|
21
|
+
|
22
|
+
def run(*args)
|
23
|
+
# wipe the task runs before every run; that way `output_task_runs` outputs
|
24
|
+
# just this run's task runs
|
25
|
+
self.runs.clear
|
26
|
+
|
27
|
+
# increment the run num and run the task
|
28
|
+
@run_num += 1
|
29
|
+
task = super
|
30
|
+
|
31
|
+
# recursively output the task runs in a tree format
|
32
|
+
output_task_runs(self.runs, 0, "#{@run_num}) ".rjust(LEVEL_PREFIX.size, ' '))
|
33
|
+
|
34
|
+
# return the top-level task that was run
|
35
|
+
task
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# track all task runs
|
41
|
+
def build_and_run_task(task_class, params = nil)
|
42
|
+
task_run = TaskRun.new(task_class, params)
|
43
|
+
@task_run_stack.last.runs << task_run
|
44
|
+
|
45
|
+
@task_run_stack.push(task_run)
|
46
|
+
task = super(task_class, params)
|
47
|
+
@task_run_stack.pop
|
48
|
+
task
|
49
|
+
end
|
50
|
+
|
51
|
+
def output_task_runs(runs, level, prefix = nil)
|
52
|
+
runs.each do |task_run|
|
53
|
+
# recursively output the prefix and task class on indented, bulleted lines
|
54
|
+
@kernel.puts "#{LEVEL_PREFIX*level}" \
|
55
|
+
"#{LEVEL_BULLET if level > 0}" \
|
56
|
+
"#{prefix}#{task_run.task_class}"
|
57
|
+
output_task_runs(task_run.runs, level+1)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/lib/dk/version.rb
CHANGED
data/lib/dk.rb
CHANGED
@@ -1,4 +1,26 @@
|
|
1
|
-
require
|
1
|
+
require 'dk/version'
|
2
|
+
require 'dk/config'
|
3
|
+
require 'dk/task'
|
2
4
|
|
3
5
|
module Dk
|
6
|
+
|
7
|
+
def self.config
|
8
|
+
@config ||= Config.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.configure(&block)
|
12
|
+
self.config.init_procs << block
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.init
|
16
|
+
self.config.init
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.reset
|
20
|
+
@config = Config.new
|
21
|
+
end
|
22
|
+
|
23
|
+
NoticeError = Class.new(RuntimeError)
|
24
|
+
NoParamError = Class.new(ArgumentError)
|
25
|
+
|
4
26
|
end
|
data/test/helper.rb
CHANGED
@@ -2,13 +2,18 @@
|
|
2
2
|
# put any test helpers here
|
3
3
|
|
4
4
|
# add the root dir to the load path
|
5
|
-
|
5
|
+
require 'pathname'
|
6
|
+
ROOT_PATH = Pathname.new(File.expand_path("../..", __FILE__))
|
7
|
+
$LOAD_PATH.unshift(ROOT_PATH)
|
6
8
|
|
7
9
|
# require pry for debugging (`binding.pry`)
|
8
10
|
require 'pry'
|
9
11
|
|
10
12
|
require 'test/support/factory'
|
11
13
|
|
14
|
+
# put scmd in test mode
|
15
|
+
ENV['SCMD_TEST_MODE'] = '1'
|
16
|
+
|
12
17
|
# 1.8.7 backfills
|
13
18
|
|
14
19
|
# Array#sample
|
data/test/support/factory.rb
CHANGED
@@ -3,4 +3,42 @@ require 'assert/factory'
|
|
3
3
|
module Factory
|
4
4
|
extend Assert::Factory
|
5
5
|
|
6
|
+
def self.ssh_cmd_opts
|
7
|
+
cmd_opts = { Factory.string => Factory.string }
|
8
|
+
# optionally have these options, this will ensure the default ssh cmd opts
|
9
|
+
# are used if these aren't provided
|
10
|
+
if Factory.boolean
|
11
|
+
cmd_opts.merge!({
|
12
|
+
:hosts => Factory.hosts,
|
13
|
+
:ssh_args => Factory.string,
|
14
|
+
:host_ssh_args => { Factory.string => Factory.string }
|
15
|
+
})
|
16
|
+
end
|
17
|
+
cmd_opts
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.stdout
|
21
|
+
Factory.integer(3).times.map{ Factory.string }.join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.stderr
|
25
|
+
Factory.integer(3).times.map{ Factory.string }.join("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.exitstatus
|
29
|
+
[0, 1].sample
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.hosts
|
33
|
+
Factory.integer(3).times.map{ "#{Factory.string}.example.com" }
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.log_file
|
37
|
+
ROOT_PATH.join("test/support/log/#{Factory.string}.txt")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.task_callback(task_class, params = nil)
|
41
|
+
Dk::Task::Callback.new(task_class, params || {})
|
42
|
+
end
|
43
|
+
|
6
44
|
end
|
File without changes
|