gene_system 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,6 +18,11 @@ module GeneSystem
18
18
  File.read(file_path)
19
19
  )
20
20
 
21
+ if missing_required?(manifest)
22
+ raise 'manifest is missing required attributes name, '\
23
+ 'version and/or metadata'
24
+ end
25
+
21
26
  if incompatible?(manifest)
22
27
  raise 'provided manifest is invalid or incompatible with '\
23
28
  'this version of gene_system'
@@ -29,6 +34,34 @@ module GeneSystem
29
34
  )
30
35
  end
31
36
 
37
+ ##
38
+ # Determines whether there are missing
39
+ # attributes in given manifest.
40
+ #
41
+ # Manifests require name, version and
42
+ # metadata attributes.
43
+ #
44
+ # If these are not present in the given manifest
45
+ # then this method returns true, indicating
46
+ # that the manifest does not have all required
47
+ # attributes and is therefore not valid.
48
+ #
49
+ # If they are present, then false will be returned
50
+ # indicating that the manifest has required
51
+ # attributes
52
+ #
53
+ # @param [GeneSystem::Manifest] manifest
54
+ #
55
+ # @return [Boolean]
56
+ #
57
+ def missing_required?(manifest)
58
+ return true unless manifest['name']
59
+ return true unless manifest['version']
60
+ return true unless manifest['metadata']
61
+
62
+ false
63
+ end
64
+
32
65
  ##
33
66
  # Incompatible returns true if the current manifest is not compatible
34
67
  # with this version of GeneSystem.
@@ -57,6 +90,15 @@ module GeneSystem
57
90
  @steps = GeneSystem::Step.load_steps(@data.steps)
58
91
  end
59
92
 
93
+ ##
94
+ # Manifest name and version getter
95
+ #
96
+ # @return [String]
97
+ #
98
+ def name_and_version
99
+ "#{name} v#{version}"
100
+ end
101
+
60
102
  ##
61
103
  # Manifest name getter
62
104
  #
@@ -110,7 +152,7 @@ module GeneSystem
110
152
  #
111
153
  # By default a all steps will be returned.
112
154
  #
113
- # @example
155
+ # Example:
114
156
  # query = ->(step) { step.tags.include?("foo") }
115
157
  # manifest.steps(query)
116
158
  #
@@ -123,5 +165,20 @@ module GeneSystem
123
165
  query.call(step)
124
166
  end
125
167
  end
168
+
169
+ ##
170
+ # Returns manifest information and variables
171
+ #
172
+ # @return [Hashie::Mash]
173
+ #
174
+ def variables
175
+ Hashie::Mash.new(
176
+ 'manifest' => {
177
+ 'name' => name,
178
+ 'version' => version,
179
+ 'metadata' => metadata
180
+ }
181
+ )
182
+ end
126
183
  end
127
184
  end
@@ -1,5 +1,6 @@
1
1
  require 'os'
2
2
  require 'ruby-handlebars'
3
+ require 'cgi'
3
4
 
4
5
  module GeneSystem
5
6
  # Platform is a class to handle command execution on host system
@@ -1,3 +1,3 @@
1
1
  module GeneSystem
2
- VERSION = '0.3.2'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
data/manifest.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "manifest.json",
3
+ "version": "0.0.1",
4
+ "metadata": {
5
+ "gene_system": {
6
+ "version": "0.3.2"
7
+ }
8
+ },
9
+ "steps": [
10
+ {
11
+ "name": "greet user",
12
+ "exe": {
13
+ "install": {
14
+ "prompts": [
15
+ {
16
+ "prompt": "Please enter your name",
17
+ "var": "name"
18
+ }
19
+ ],
20
+ "cmd": [
21
+ "echo hello '{{name}}'"
22
+ ]
23
+ },
24
+ "remove": {
25
+ "prompts": [
26
+ {
27
+ "prompt": "Please enter your name",
28
+ "var": "name"
29
+ }
30
+ ],
31
+ "cmd": [
32
+ "echo 'goodbye {{name}}'"
33
+ ]
34
+ }
35
+ },
36
+ "tags": "example step"
37
+ },
38
+ {
39
+ "name": "print version",
40
+ "exe": {
41
+ "install": {
42
+ "cmd": [
43
+ "echo '{{manifest.name}}'",
44
+ "echo '{{manifest.version}}'",
45
+ "echo '{{manifest.metadata.gene_system.version}}'"
46
+ ]
47
+ },
48
+ "remove": {
49
+ "cmd": [
50
+ "echo '{{manifest.name}}'",
51
+ "echo '{{manifest.version}}'",
52
+ "echo '{{manifest.metadata.gene_system.version}}'"
53
+ ]
54
+ }
55
+ },
56
+ "tags": "example front matter access"
57
+ }
58
+ ]
59
+ }
@@ -1,83 +1,105 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe GeneSystem::CLI do
4
- let(:logger) { double(Logger) }
5
- let(:err_logger) { double(Logger) }
6
- let(:message) { 'All work and no play makes Jack a dull boy' }
4
+ subject { described_class.new }
7
5
 
8
- before do
9
- allow(described_class).to receive(:exit).and_return(double)
10
- end
6
+ describe '#version' do
7
+ let(:version) { double(GeneSystem::Commands::PrintVersion) }
8
+ let(:options) { double }
11
9
 
12
- describe '.logger' do
13
- let(:out) { double(STDOUT) }
14
10
  before do
15
- allow(Logger).to receive(:new).and_return(logger)
16
- allow(logger).to receive(:formatter=)
17
- subject.logger(out)
18
- end
11
+ allow(subject).to receive(:options).and_return(options)
12
+ allow(GeneSystem::Commands::PrintVersion)
13
+ .to receive(:new)
14
+ .and_return(version)
19
15
 
20
- it 'creates a new logger to STDOUT' do
21
- expect(Logger).to have_received(:new).with(out)
16
+ allow(version).to receive(:run)
17
+ subject.version
22
18
  end
23
- end
24
19
 
25
- describe '.print_message' do
26
- let(:logger) { double(Logger) }
27
-
28
- before do
29
- allow(described_class).to receive(:logger).and_return(logger)
30
- allow(logger).to receive(:info)
31
- described_class.print_message(message)
20
+ it 'creates a new version command' do
21
+ expect(GeneSystem::Commands::PrintVersion)
22
+ .to have_received(:new)
23
+ .with(options)
32
24
  end
33
25
 
34
- it 'prints message to stdout' do
35
- expect(logger).to have_received(:info).with(message)
26
+ it 'calls run' do
27
+ expect(version).to have_received(:run)
36
28
  end
37
29
  end
38
30
 
39
- describe '.print_warning' do
40
- let(:logger) { double(Logger) }
31
+ describe '#new' do
32
+ let(:new) { double(GeneSystem::Commands::CreateManifest) }
33
+ let(:options) { double }
41
34
 
42
35
  before do
43
- allow(described_class).to receive(:logger).and_return(logger)
44
- allow(logger).to receive(:warn)
45
- described_class.print_warning(message)
36
+ allow(subject).to receive(:options).and_return(options)
37
+ allow(GeneSystem::Commands::CreateManifest)
38
+ .to receive(:new)
39
+ .and_return(new)
40
+
41
+ allow(new).to receive(:run)
42
+ subject.new
46
43
  end
47
44
 
48
- it 'prints message to stdout' do
49
- expect(logger).to have_received(:warn).with(message)
45
+ it 'creates a new new command' do
46
+ expect(GeneSystem::Commands::CreateManifest)
47
+ .to have_received(:new)
48
+ .with(options)
49
+ end
50
+
51
+ it 'calls run' do
52
+ expect(new).to have_received(:run)
50
53
  end
51
54
  end
52
55
 
53
- describe '.print_error' do
54
- let(:logger) { double(Logger) }
56
+ describe '#install' do
57
+ let(:install) { double(GeneSystem::Commands::InstallManifest) }
58
+ let(:options) { double }
55
59
 
56
60
  before do
57
- allow(described_class).to receive(:logger).and_return(logger)
58
- allow(logger).to receive(:error)
59
- described_class.print_error(message)
61
+ allow(subject).to receive(:options).and_return(options)
62
+ allow(GeneSystem::Commands::InstallManifest)
63
+ .to receive(:new)
64
+ .and_return(install)
65
+
66
+ allow(install).to receive(:run)
67
+ subject.install
60
68
  end
61
69
 
62
- it 'prints message to stderr' do
63
- expect(logger).to have_received(:error).with(message)
70
+ it 'creates a new install command' do
71
+ expect(GeneSystem::Commands::InstallManifest)
72
+ .to have_received(:new)
73
+ .with(options)
74
+ end
75
+
76
+ it 'calls run' do
77
+ expect(install).to have_received(:run)
64
78
  end
65
79
  end
66
80
 
67
- describe '.print_message_and_exit' do
68
- let(:exit_code) { 2 }
81
+ describe '#remove' do
82
+ let(:remove) { double(GeneSystem::Commands::RemoveManifest) }
83
+ let(:options) { double }
69
84
 
70
85
  before do
71
- allow(described_class).to receive(:print_message)
72
- described_class.print_message_and_exit(message, exit_code)
86
+ allow(subject).to receive(:options).and_return(options)
87
+ allow(GeneSystem::Commands::RemoveManifest)
88
+ .to receive(:new)
89
+ .and_return(remove)
90
+
91
+ allow(remove).to receive(:run)
92
+ subject.remove
73
93
  end
74
94
 
75
- it 'prints message' do
76
- expect(described_class).to have_received(:print_message).with(message)
95
+ it 'creates a new remove command' do
96
+ expect(GeneSystem::Commands::RemoveManifest)
97
+ .to have_received(:new)
98
+ .with(options)
77
99
  end
78
100
 
79
- it 'exits with set status' do
80
- expect(described_class).to have_received(:exit).with(exit_code)
101
+ it 'calls run' do
102
+ expect(remove).to have_received(:run)
81
103
  end
82
104
  end
83
105
  end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe GeneSystem::Commands::CreateManifest do
4
+ let(:name_opt) { 'manifest.jsonnet' }
5
+ let(:output_opt) { '/path/to/output' }
6
+
7
+ let(:options) do
8
+ Hashie::Mash.new(
9
+ name: name_opt,
10
+ out: output_opt
11
+ )
12
+ end
13
+
14
+ subject { described_class.new(options) }
15
+
16
+ describe '#run' do
17
+ let(:prompt) { double }
18
+ let(:prompt_response) { nil }
19
+
20
+ let(:is_dir) { true }
21
+
22
+ before do
23
+ subject.instance_variable_set(:@prompt, prompt)
24
+
25
+ allow(File).to receive(:directory?).and_return(is_dir)
26
+ allow(subject).to receive(:puts)
27
+
28
+ allow(prompt).to receive(:ask)
29
+ .and_return(prompt_response)
30
+
31
+ allow(GeneSystem::Generators)
32
+ .to receive(:render_empty_manifest)
33
+ end
34
+
35
+ context 'when name and output prompt is provided' do
36
+ before { subject.run }
37
+
38
+ it 'does not ask for any input' do
39
+ expect(prompt).not_to have_received(:ask)
40
+ end
41
+
42
+ it 'renders empty manifest' do
43
+ expect(GeneSystem::Generators)
44
+ .to have_received(:render_empty_manifest)
45
+ .with(name_opt, output_opt)
46
+ end
47
+
48
+ it 'reports success' do
49
+ expect(subject)
50
+ .to have_received(:puts)
51
+ .with('✔ Manifest successfully created in /path/to/output')
52
+ end
53
+ end
54
+
55
+ context 'when only name is provided' do
56
+ let(:output_opt) { nil }
57
+ let(:prompt_response) { '/path/to/given/output' }
58
+
59
+ before { subject.run }
60
+
61
+ it 'prompts for output location' do
62
+ expect(prompt).to have_received(:ask)
63
+ .with(
64
+ 'Please specify output location',
65
+ default: Dir.pwd
66
+ )
67
+ end
68
+
69
+ it 'renders empty manifest' do
70
+ expect(GeneSystem::Generators)
71
+ .to have_received(:render_empty_manifest)
72
+ .with(name_opt, prompt_response)
73
+ end
74
+
75
+ it 'reports success' do
76
+ expect(subject)
77
+ .to have_received(:puts)
78
+ .with('✔ Manifest successfully created in /path/to/given/output')
79
+ end
80
+ end
81
+
82
+ context 'when only output is provided' do
83
+ let(:name_opt) { nil }
84
+ let(:prompt_response) { 'abc.json' }
85
+
86
+ before { subject.run }
87
+
88
+ it 'prompts for manifest name' do
89
+ expect(prompt).to have_received(:ask)
90
+ .with(
91
+ 'Please enter the name of the manifest',
92
+ default: described_class::DEFAULT_MANIFEST_NAME
93
+ )
94
+ end
95
+
96
+ it 'renders empty manifest' do
97
+ expect(GeneSystem::Generators)
98
+ .to have_received(:render_empty_manifest)
99
+ .with(prompt_response, output_opt)
100
+ end
101
+
102
+ it 'reports success' do
103
+ expect(subject)
104
+ .to have_received(:puts)
105
+ .with('✔ Manifest successfully created in /path/to/output')
106
+ end
107
+ end
108
+
109
+ context 'when the output location is not a directory' do
110
+ let(:is_dir) { false }
111
+
112
+ it 'raises runtime error' do
113
+ expect do
114
+ subject.run
115
+ end.to raise_error('output location must be a folder')
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,258 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe GeneSystem::Commands::InstallManifest do
4
+ let(:options) { Hashie::Mash.new }
5
+ let(:prompt) { double(TTY::Prompt) }
6
+ let(:version) { '1.0.0' }
7
+ let(:execute_command_result) { 0 }
8
+ let(:manifest_name) { 'some manifest' }
9
+
10
+ subject { described_class.new(options) }
11
+
12
+ before do
13
+ subject.instance_variable_set(:@prompt, prompt)
14
+ end
15
+
16
+ describe '#run' do
17
+ let(:platform) { double(GeneSystem::Platform) }
18
+
19
+ let(:manifest) do
20
+ double(
21
+ GeneSystem::Manifest,
22
+ name: manifest_name,
23
+ name_and_version: "#{manifest_name} v#{version}"
24
+ )
25
+ end
26
+
27
+ let(:skip_command) { 'false' }
28
+ let(:prompts) { [prompt_definition, prompt_definition] }
29
+ let(:prompt_definition) do
30
+ {
31
+ "prompt": 'Please enter your name',
32
+ "var": 'name'
33
+ }
34
+ end
35
+ let(:cmd) { 'echo {{name}}' }
36
+ let(:cmds) do
37
+ [
38
+ cmd
39
+ ]
40
+ end
41
+
42
+ let(:step_definition) do
43
+ {
44
+ "name": 'some step',
45
+ "exe": {
46
+ "install": {
47
+ "skip": skip_command,
48
+ "prompts": prompts,
49
+ "cmd": cmds
50
+ }
51
+ }
52
+ }
53
+ end
54
+
55
+ let(:step) do
56
+ GeneSystem::Step.new(step_definition)
57
+ end
58
+
59
+ before do
60
+ allow(GeneSystem::Manifest)
61
+ .to receive(:new_from_file)
62
+ .and_return(manifest)
63
+
64
+ allow(GeneSystem::Platform)
65
+ .to receive(:new)
66
+ .and_return(platform)
67
+
68
+ allow(manifest)
69
+ .to receive(:steps)
70
+ .and_return([step, step])
71
+
72
+ allow(manifest)
73
+ .to receive(:version)
74
+ .and_return(version)
75
+
76
+ allow(manifest)
77
+ .to receive(:variables)
78
+ .and_return({})
79
+
80
+ allow(platform)
81
+ .to receive(:execute_commands)
82
+
83
+ allow(platform)
84
+ .to receive(:execute_command)
85
+ .and_return(execute_command_result)
86
+
87
+ allow(prompt).to receive(:ask).and_return(prompt_response)
88
+ allow(subject).to receive(:puts)
89
+ end
90
+
91
+ context 'when manifest path is provided' do
92
+ let(:manifest_path) { 'provided/path/to/manifest.json' }
93
+ let(:execute_command_result) { 1 }
94
+ let(:prompt_response) { 'Jon' }
95
+ let(:options) do
96
+ Hashie::Mash.new(
97
+ manifest: manifest_path
98
+ )
99
+ end
100
+
101
+ before do
102
+ subject.run
103
+ end
104
+
105
+ it 'loads manifest from expected file' do
106
+ expect(GeneSystem::Manifest)
107
+ .to have_received(:new_from_file)
108
+ .with(manifest_path)
109
+ end
110
+
111
+ it 'prints install message' do
112
+ expect(subject)
113
+ .to have_received(:puts)
114
+ .with("INSTALL #{manifest_name} v#{version}")
115
+ end
116
+
117
+ describe 'skip steps' do
118
+ context 'when there is a skip command defined' do
119
+ it 'checks whether it should skip any steps' do
120
+ expect(platform)
121
+ .to have_received(:execute_command)
122
+ .with(skip_command).twice
123
+ end
124
+ end
125
+
126
+ context 'when there is no skip command defined' do
127
+ let(:step_definition) do
128
+ {
129
+ "name": 'some step',
130
+ "exe": {
131
+ "install": {
132
+ "skip": nil,
133
+ "cmd": []
134
+ }
135
+ }
136
+ }
137
+ end
138
+
139
+ it 'does not try to run nil command' do
140
+ expect(platform)
141
+ .not_to have_received(:execute_command)
142
+ end
143
+ end
144
+
145
+ context 'when the skip command returns non zero' do
146
+ let(:execute_command_result) { 1 }
147
+ let(:prompts) { [] }
148
+
149
+ it 'does not execute step' do
150
+ expect(platform)
151
+ .not_to have_received(:execute_command)
152
+ .with(cmd)
153
+ end
154
+ end
155
+ end
156
+
157
+ describe 'prompting user for input' do
158
+ context 'when there are no prompts' do
159
+ let(:prompts) { [] }
160
+
161
+ it 'does not ask user for input' do
162
+ expect(prompt)
163
+ .not_to have_received(:ask)
164
+ end
165
+ end
166
+
167
+ context 'when there are prompts' do
168
+ it 'asks user for input' do
169
+ expect(prompt)
170
+ .to have_received(:ask)
171
+ .with('Please enter your name').exactly(4).times
172
+ end
173
+ end
174
+ end
175
+
176
+ it 'executes step commands' do
177
+ expect(platform)
178
+ .to have_received(:execute_commands)
179
+ .with(cmds, 'name' => prompt_response).twice
180
+ end
181
+
182
+ it 'prints success message' do
183
+ expect(subject)
184
+ .to have_received(:puts)
185
+ .with("✔ Manifest #{manifest_name} v#{version} successfully installed")
186
+ end
187
+ end
188
+
189
+ context 'when the manifest name is not provided' do
190
+ let(:prompt_response) { '/path/to/manifest.json' }
191
+
192
+ before do
193
+ allow(prompt)
194
+ .to receive(:ask)
195
+ .and_return(prompt_response)
196
+
197
+ subject.run
198
+ end
199
+
200
+ describe 'prompting user for manifest path' do
201
+ it 'prompts user for manifest path' do
202
+ expect(prompt)
203
+ .to have_received(:ask)
204
+ .with(
205
+ 'Please enter the path to the configuration manifest',
206
+ default: 'manifest.json'
207
+ )
208
+ end
209
+ end
210
+
211
+ describe 'skipping steps' do
212
+ context 'when there is a skip command defined' do
213
+ it 'checks whether it should skip any steps' do
214
+ expect(platform)
215
+ .to have_received(:execute_command)
216
+ .with(skip_command).twice
217
+ end
218
+ end
219
+
220
+ context 'when there is no skip command defined' do
221
+ let(:step_definition) do
222
+ {
223
+ "name": 'some step',
224
+ "exe": {
225
+ "install": {
226
+ "skip": nil,
227
+ "cmd": []
228
+ }
229
+ }
230
+ }
231
+ end
232
+
233
+ it 'does not try to run nil command' do
234
+ expect(platform)
235
+ .not_to have_received(:execute_command)
236
+ end
237
+ end
238
+
239
+ context 'when the skip command returns non zero' do
240
+ let(:execute_command_result) { 1 }
241
+ let(:prompts) { [] }
242
+
243
+ it 'does not execute step' do
244
+ expect(platform)
245
+ .not_to have_received(:execute_command)
246
+ .with(cmd)
247
+ end
248
+ end
249
+ end
250
+
251
+ it 'prints success message' do
252
+ expect(subject)
253
+ .to have_received(:puts)
254
+ .with("✔ Manifest #{manifest_name} v#{version} successfully installed")
255
+ end
256
+ end
257
+ end
258
+ end