producer-core 0.3.7 → 0.3.8
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/features/cli_usage.feature +6 -1
- data/features/registry.feature +12 -0
- data/features/task_nested_tasks.feature +5 -0
- data/lib/producer/core/cli.rb +38 -24
- data/lib/producer/core/env.rb +3 -1
- data/lib/producer/core/errors.rb +1 -0
- data/lib/producer/core/version.rb +1 -1
- data/lib/producer/core/worker.rb +13 -5
- data/lib/producer/core.rb +1 -0
- data/spec/producer/core/cli_spec.rb +71 -57
- data/spec/producer/core/env_spec.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a595e50ec520bdedf5e94b927b4dd8080a1acef
|
4
|
+
data.tar.gz: 1c7c1f218276ed668d62c2d31f482e12a2f3ca40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 727921d665f949d90cb092b3c653c698a7f1c82d2d48e1dd8f73adbb9905fd60c3b0348a6bfdc3c1e01b77c35f7843507768c3f18102d3e5e9ae8d16b748b9a6
|
7
|
+
data.tar.gz: b2ece9b39a0be51041fd89f1b526c2d8042a252c3e4961be6cb9c621420c1f5bbccfab531df0885b3fa8f29473c07c32de8f30d46b2a6f7332bdccff2b2f9d73
|
data/features/cli_usage.feature
CHANGED
@@ -6,5 +6,10 @@ Feature: CLI usage
|
|
6
6
|
Then the exit status must be 64
|
7
7
|
And the output must contain exactly:
|
8
8
|
"""
|
9
|
-
Usage: producer [
|
9
|
+
Usage: producer [options] [recipes]
|
10
|
+
|
11
|
+
options:
|
12
|
+
-v, --verbose enable verbose mode
|
13
|
+
-n, --dry-run enable dry run mode
|
14
|
+
-t, --target HOST target host
|
10
15
|
"""
|
data/features/registry.feature
CHANGED
@@ -34,3 +34,15 @@ Feature: key/value registry
|
|
34
34
|
"""
|
35
35
|
When I successfully execute the recipe
|
36
36
|
Then the output must contain "some_value"
|
37
|
+
|
38
|
+
Scenario: `get' keyword should trigger an error when given invalid key
|
39
|
+
Given a recipe with:
|
40
|
+
"""
|
41
|
+
task :some_task do
|
42
|
+
echo get :no_key
|
43
|
+
echo 'after_fail'
|
44
|
+
end
|
45
|
+
"""
|
46
|
+
When I execute the recipe
|
47
|
+
Then the output must not contain "after_fail"
|
48
|
+
And the output must match /\A\w+Error:\s+:no_key/
|
@@ -13,3 +13,8 @@ Feature: nested tasks
|
|
13
13
|
Scenario: applies nested tasks
|
14
14
|
When I successfully execute the recipe
|
15
15
|
Then the output must match /\AOK/
|
16
|
+
|
17
|
+
Scenario: indents logging from nested tasks
|
18
|
+
When I successfully execute the recipe with option -v
|
19
|
+
Then the output must match /^ Task:.+/
|
20
|
+
And the output must match /^ action:.+/
|
data/lib/producer/core/cli.rb
CHANGED
@@ -3,8 +3,7 @@ module Producer
|
|
3
3
|
class CLI
|
4
4
|
ArgumentError = Class.new(::ArgumentError)
|
5
5
|
|
6
|
-
|
7
|
-
USAGE = "Usage: #{File.basename $0} #{OPTIONS_USAGE} recipe_file".freeze
|
6
|
+
USAGE = "Usage: #{File.basename $0} [options] [recipes]".freeze
|
8
7
|
|
9
8
|
EX_USAGE = 64
|
10
9
|
EX_SOFTWARE = 70
|
@@ -15,8 +14,8 @@ module Producer
|
|
15
14
|
begin
|
16
15
|
cli.parse_arguments!
|
17
16
|
cli.run
|
18
|
-
rescue ArgumentError
|
19
|
-
stderr.puts
|
17
|
+
rescue ArgumentError => e
|
18
|
+
stderr.puts e.message
|
20
19
|
exit EX_USAGE
|
21
20
|
rescue RuntimeError => e
|
22
21
|
stderr.puts "#{e.class.name.split('::').last}: #{e.message}"
|
@@ -25,40 +24,55 @@ module Producer
|
|
25
24
|
end
|
26
25
|
end
|
27
26
|
|
28
|
-
attr_reader :arguments, :env
|
27
|
+
attr_reader :arguments, :stdin, :stdout, :stderr, :env
|
29
28
|
|
30
29
|
def initialize(args, stdin: $stdin, stdout: $stdout, stderr: $stderr)
|
31
30
|
@arguments = args
|
32
31
|
@stdin = stdin
|
33
32
|
@stdout = stdout
|
34
|
-
@
|
33
|
+
@stderr = stderr
|
34
|
+
@env = build_env
|
35
35
|
end
|
36
36
|
|
37
37
|
def parse_arguments!
|
38
|
-
@arguments
|
39
|
-
|
40
|
-
when '-v'
|
41
|
-
env.verbose = true
|
42
|
-
when '-n'
|
43
|
-
env.dry_run = true
|
44
|
-
when '-t'
|
45
|
-
env.target = arguments.delete_at i + 1
|
46
|
-
else
|
47
|
-
m << e
|
48
|
-
end
|
49
|
-
m
|
50
|
-
end
|
51
|
-
|
52
|
-
fail ArgumentError unless @arguments.any?
|
38
|
+
option_parser.parse!(@arguments)
|
39
|
+
fail ArgumentError, option_parser if @arguments.empty?
|
53
40
|
end
|
54
41
|
|
55
42
|
def run(worker: Worker.new(@env))
|
56
|
-
worker.process recipe.tasks
|
43
|
+
evaluate_recipes.each { |recipe| worker.process recipe.tasks }
|
57
44
|
@env.cleanup
|
58
45
|
end
|
59
46
|
|
60
|
-
def
|
61
|
-
@
|
47
|
+
def evaluate_recipes
|
48
|
+
@arguments.map { |e| Recipe::FileEvaluator.evaluate(e, @env) }
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def build_env
|
55
|
+
Env.new(input: @stdin, output: @stdout, error_output: @stderr)
|
56
|
+
end
|
57
|
+
|
58
|
+
def option_parser
|
59
|
+
OptionParser.new do |opts|
|
60
|
+
opts.banner = USAGE
|
61
|
+
opts.separator ''
|
62
|
+
opts.separator 'options:'
|
63
|
+
|
64
|
+
opts.on '-v', '--verbose', 'enable verbose mode' do |e|
|
65
|
+
env.verbose = true
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on '-n', '--dry-run', 'enable dry run mode' do |e|
|
69
|
+
env.dry_run = true
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on '-t', '--target HOST', 'target host' do |e|
|
73
|
+
env.target = e
|
74
|
+
end
|
75
|
+
end
|
62
76
|
end
|
63
77
|
end
|
64
78
|
end
|
data/lib/producer/core/env.rb
CHANGED
data/lib/producer/core/errors.rb
CHANGED
data/lib/producer/core/worker.rb
CHANGED
@@ -14,21 +14,29 @@ module Producer
|
|
14
14
|
tasks.each { |t| process_task t }
|
15
15
|
end
|
16
16
|
|
17
|
-
def process_task(task)
|
17
|
+
def process_task(task, indent_level = 0)
|
18
18
|
if task.condition_met?
|
19
|
-
|
19
|
+
log "Task: `#{task}' applying...", indent_level
|
20
20
|
task.actions.each do |e|
|
21
21
|
case e
|
22
|
-
when Task then process_task e
|
22
|
+
when Task then process_task e, indent_level + 2
|
23
23
|
else
|
24
|
-
|
24
|
+
log " action: #{e}", indent_level
|
25
25
|
e.apply unless @env.dry_run?
|
26
26
|
end
|
27
27
|
end
|
28
28
|
else
|
29
|
-
|
29
|
+
log "Task: `#{task}' skipped", indent_level
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def log(message, indent_level)
|
37
|
+
message = [' ' * indent_level, message].join
|
38
|
+
@env.log message
|
39
|
+
end
|
32
40
|
end
|
33
41
|
end
|
34
42
|
end
|
data/lib/producer/core.rb
CHANGED
@@ -5,28 +5,42 @@ module Producer::Core
|
|
5
5
|
include ExitHelpers
|
6
6
|
include FixturesHelpers
|
7
7
|
|
8
|
-
let(:recipe_file) { fixture_path_for 'recipes/some_recipe.rb' }
|
9
8
|
let(:options) { [] }
|
9
|
+
let(:recipe_file) { fixture_path_for 'recipes/some_recipe.rb' }
|
10
10
|
let(:arguments) { [*options, recipe_file] }
|
11
11
|
|
12
12
|
subject(:cli) { described_class.new(arguments) }
|
13
13
|
|
14
14
|
describe '.run!' do
|
15
|
-
|
16
|
-
|
15
|
+
subject(:run!) { described_class.run! arguments }
|
16
|
+
|
17
|
+
it 'builds a new CLI instance with given arguments' do
|
18
|
+
expect(described_class)
|
19
|
+
.to receive(:new).with(arguments, anything).and_call_original
|
20
|
+
run!
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'parses new CLI instance arguments' do
|
24
|
+
expect_any_instance_of(described_class).to receive :parse_arguments!
|
25
|
+
run!
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'runs the new CLI instance' do
|
29
|
+
expect_any_instance_of(described_class).to receive :run
|
30
|
+
run!
|
31
|
+
end
|
17
32
|
|
18
|
-
context 'when
|
33
|
+
context 'when no recipe is given' do
|
19
34
|
let(:arguments) { [] }
|
20
35
|
|
21
36
|
it 'exits with a return status of 64' do
|
22
|
-
expect { run!
|
23
|
-
expect(e.status).to eq 64
|
24
|
-
end
|
37
|
+
expect { described_class.run! arguments, stderr: StringIO.new }
|
38
|
+
.to raise_error(SystemExit) { |e| expect(e.status).to eq 64 }
|
25
39
|
end
|
26
40
|
|
27
41
|
it 'prints the usage on the error stream' do
|
28
|
-
trap_exit { run! }
|
29
|
-
|
42
|
+
expect { trap_exit { run! } }
|
43
|
+
.to output(/\AUsage: .+/).to_stderr
|
30
44
|
end
|
31
45
|
end
|
32
46
|
|
@@ -34,92 +48,95 @@ module Producer::Core
|
|
34
48
|
let(:recipe_file) { fixture_path_for 'recipes/raise.rb' }
|
35
49
|
|
36
50
|
it 'exits with a return status of 70' do
|
37
|
-
expect { run!
|
38
|
-
expect(e.status).to eq 70
|
39
|
-
end
|
51
|
+
expect { described_class.run! arguments, stderr: StringIO.new }
|
52
|
+
.to raise_error(SystemExit) { |e| expect(e.status).to eq 70 }
|
40
53
|
end
|
41
54
|
|
42
55
|
it 'prints exception name and message and the error stream' do
|
43
|
-
trap_exit { run! }
|
44
|
-
|
56
|
+
expect { trap_exit { run! } }
|
57
|
+
.to output("RemoteCommandExecutionError: false\n").to_stderr
|
45
58
|
end
|
46
59
|
end
|
47
60
|
end
|
48
61
|
|
49
|
-
describe '#
|
50
|
-
|
51
|
-
let(:stdout) { StringIO.new }
|
52
|
-
let(:stderr) { StringIO.new }
|
53
|
-
|
54
|
-
subject(:cli) { described_class.new(arguments,
|
55
|
-
stdin: stdin, stdout: stdout, stderr: stderr) }
|
56
|
-
|
57
|
-
it 'returns an env' do
|
62
|
+
describe '#initialize' do
|
63
|
+
it 'assigns an env' do
|
58
64
|
expect(cli.env).to be_an Env
|
59
65
|
end
|
60
66
|
|
61
67
|
it 'assigns CLI stdin as the env input' do
|
62
|
-
expect(cli.env.input).to be stdin
|
68
|
+
expect(cli.env.input).to be cli.stdin
|
63
69
|
end
|
64
70
|
|
65
71
|
it 'assigns CLI stdout as the env output' do
|
66
|
-
expect(cli.env.output).to be stdout
|
72
|
+
expect(cli.env.output).to be cli.stdout
|
67
73
|
end
|
68
74
|
|
69
75
|
it 'assigns CLI stderr as the env error output' do
|
70
|
-
expect(cli.env.error_output).to be stderr
|
76
|
+
expect(cli.env.error_output).to be cli.stderr
|
71
77
|
end
|
72
78
|
end
|
73
79
|
|
74
80
|
describe '#parse_arguments!' do
|
75
|
-
|
76
|
-
|
81
|
+
let(:options) { %w[-v -t some_host.example] }
|
82
|
+
|
83
|
+
it 'removes options from arguments' do
|
84
|
+
cli.parse_arguments!
|
85
|
+
expect(cli.arguments).to eq [recipe_file]
|
86
|
+
end
|
77
87
|
|
78
|
-
|
88
|
+
context 'with verbose option' do
|
89
|
+
let(:options) { %w[-v] }
|
79
90
|
|
80
|
-
it '
|
81
|
-
|
91
|
+
it 'enables env verbose mode' do
|
92
|
+
cli.parse_arguments!
|
93
|
+
expect(cli.env).to be_verbose
|
82
94
|
end
|
95
|
+
end
|
83
96
|
|
84
|
-
|
85
|
-
|
97
|
+
context 'with dry run option' do
|
98
|
+
let(:options) { %w[-n] }
|
86
99
|
|
87
|
-
|
88
|
-
|
89
|
-
|
100
|
+
it 'enables env dry run mode' do
|
101
|
+
cli.parse_arguments!
|
102
|
+
expect(cli.env).to be_dry_run
|
90
103
|
end
|
104
|
+
end
|
91
105
|
|
92
|
-
|
93
|
-
|
106
|
+
context 'with target option' do
|
107
|
+
let(:options) { %w[-t some_host.example] }
|
94
108
|
|
95
|
-
|
96
|
-
|
97
|
-
|
109
|
+
it 'assigns the given target to the env' do
|
110
|
+
cli.parse_arguments!
|
111
|
+
expect(cli.env.target).to eq 'some_host.example'
|
98
112
|
end
|
113
|
+
end
|
99
114
|
|
100
|
-
|
101
|
-
|
115
|
+
context 'with combined options' do
|
116
|
+
let(:options) { %w[-vn]}
|
102
117
|
|
103
|
-
|
104
|
-
|
105
|
-
|
118
|
+
it 'handles combined options' do
|
119
|
+
cli.parse_arguments!
|
120
|
+
expect(cli.env).to be_verbose.and be_dry_run
|
106
121
|
end
|
107
122
|
end
|
108
123
|
|
109
|
-
context '
|
124
|
+
context 'when no arguments remains after parsing' do
|
110
125
|
let(:arguments) { [] }
|
111
126
|
|
112
|
-
it 'raises
|
113
|
-
expect { cli.parse_arguments! }
|
127
|
+
it 'raises an error' do
|
128
|
+
expect { cli.parse_arguments! }
|
129
|
+
.to raise_error described_class::ArgumentError
|
114
130
|
end
|
115
131
|
end
|
116
132
|
end
|
117
133
|
|
118
134
|
describe '#run' do
|
119
|
-
it 'processes
|
135
|
+
it 'processes recipes tasks with a worker' do
|
120
136
|
worker = instance_spy Worker
|
121
137
|
cli.run worker: worker
|
122
|
-
expect(worker).to have_received(:process)
|
138
|
+
expect(worker).to have_received(:process)
|
139
|
+
.with all be_an_instance_of Task
|
123
140
|
end
|
124
141
|
|
125
142
|
it 'cleans up the env' do
|
@@ -128,12 +145,9 @@ module Producer::Core
|
|
128
145
|
end
|
129
146
|
end
|
130
147
|
|
131
|
-
describe '#
|
132
|
-
it 'returns the evaluated
|
133
|
-
expect(cli.
|
134
|
-
an_object_having_attributes(name: :some_task),
|
135
|
-
an_object_having_attributes(name: :another_task)
|
136
|
-
]
|
148
|
+
describe '#evaluate_recipes' do
|
149
|
+
it 'returns the evaluated recipes' do
|
150
|
+
expect(cli.evaluate_recipes).to all be_an_instance_of Recipe
|
137
151
|
end
|
138
152
|
end
|
139
153
|
end
|
@@ -107,6 +107,10 @@ module Producer::Core
|
|
107
107
|
it 'returns the value indexed by given key from the registry' do
|
108
108
|
expect(env[:some_key]).to eq :some_value
|
109
109
|
end
|
110
|
+
|
111
|
+
it 'raises an error when given invalid key' do
|
112
|
+
expect { env[:no_key] }.to raise_error RegistryKeyError
|
113
|
+
end
|
110
114
|
end
|
111
115
|
|
112
116
|
describe '#[]=' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: producer-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thibault Jouan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: net-ssh
|