gene_system 0.3.2 → 0.4.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.
@@ -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