gene_system 0.3.2
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 +7 -0
- data/.doxie.json +25 -0
- data/.github/workflows/build.yml +25 -0
- data/.gitignore +20 -0
- data/.rubocop.yml +20 -0
- data/.ruby-version +1 -0
- data/CONTRIBUTING.md +51 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +150 -0
- data/LICENSE +21 -0
- data/README.md +255 -0
- data/Rakefile +19 -0
- data/bin/genesystem +52 -0
- data/data/manifest.jsonnet +87 -0
- data/gene_system.gemspec +40 -0
- data/lib/gene_system.rb +9 -0
- data/lib/gene_system/cli.rb +76 -0
- data/lib/gene_system/cli/commands.rb +109 -0
- data/lib/gene_system/generators.rb +58 -0
- data/lib/gene_system/manifest.rb +127 -0
- data/lib/gene_system/platform.rb +55 -0
- data/lib/gene_system/step.rb +74 -0
- data/lib/gene_system/version.rb +3 -0
- data/spec/gene_system/cli/commands_spec.rb +230 -0
- data/spec/gene_system/cli_spec.rb +83 -0
- data/spec/gene_system/generators_spec.rb +65 -0
- data/spec/gene_system/manifest_spec.rb +239 -0
- data/spec/gene_system/platform_spec.rb +94 -0
- data/spec/gene_system/step_spec.rb +108 -0
- data/spec/spec_helper.rb +27 -0
- metadata +344 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
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' }
|
7
|
+
|
8
|
+
before do
|
9
|
+
allow(described_class).to receive(:exit).and_return(double)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.logger' do
|
13
|
+
let(:out) { double(STDOUT) }
|
14
|
+
before do
|
15
|
+
allow(Logger).to receive(:new).and_return(logger)
|
16
|
+
allow(logger).to receive(:formatter=)
|
17
|
+
subject.logger(out)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'creates a new logger to STDOUT' do
|
21
|
+
expect(Logger).to have_received(:new).with(out)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
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)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'prints message to stdout' do
|
35
|
+
expect(logger).to have_received(:info).with(message)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '.print_warning' do
|
40
|
+
let(:logger) { double(Logger) }
|
41
|
+
|
42
|
+
before do
|
43
|
+
allow(described_class).to receive(:logger).and_return(logger)
|
44
|
+
allow(logger).to receive(:warn)
|
45
|
+
described_class.print_warning(message)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'prints message to stdout' do
|
49
|
+
expect(logger).to have_received(:warn).with(message)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '.print_error' do
|
54
|
+
let(:logger) { double(Logger) }
|
55
|
+
|
56
|
+
before do
|
57
|
+
allow(described_class).to receive(:logger).and_return(logger)
|
58
|
+
allow(logger).to receive(:error)
|
59
|
+
described_class.print_error(message)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'prints message to stderr' do
|
63
|
+
expect(logger).to have_received(:error).with(message)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '.print_message_and_exit' do
|
68
|
+
let(:exit_code) { 2 }
|
69
|
+
|
70
|
+
before do
|
71
|
+
allow(described_class).to receive(:print_message)
|
72
|
+
described_class.print_message_and_exit(message, exit_code)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'prints message' do
|
76
|
+
expect(described_class).to have_received(:print_message).with(message)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'exits with set status' do
|
80
|
+
expect(described_class).to have_received(:exit).with(exit_code)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe GeneSystem::Generators do
|
4
|
+
describe '::TEMPLATE_MANIFEST' do
|
5
|
+
let(:name) { 'my_dev_setup' }
|
6
|
+
|
7
|
+
before do
|
8
|
+
@manifest = Hashie::Mash.new(
|
9
|
+
described_class::TEMPLATE_MANIFEST.call(name)
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'has expected name' do
|
14
|
+
expect(@manifest.name).to eq name
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'has expected version' do
|
18
|
+
expect(@manifest.version).to eq '0.0.1'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'has gene system metadata' do
|
22
|
+
expect(@manifest.metadata.gene_system).not_to be nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'renders app version into gene system metadata' do
|
26
|
+
expect(@manifest.metadata.gene_system.version)
|
27
|
+
.to eq GeneSystem::VERSION
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'has default step' do
|
31
|
+
expect(@manifest.steps.first).to eq described_class::DEFAULT_STEP
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '.render_empty_manifest' do
|
36
|
+
let(:name) { 'new_dev_setup' }
|
37
|
+
let(:path) { 'path/to/manifests' }
|
38
|
+
|
39
|
+
let(:manifest_path) { File.join(path, "#{name}.json") }
|
40
|
+
let(:manifest_file) { double }
|
41
|
+
|
42
|
+
let(:manifest) { 'manifest' }
|
43
|
+
|
44
|
+
before do
|
45
|
+
allow(File).to receive(:open)
|
46
|
+
.with(manifest_path, 'w')
|
47
|
+
.and_yield(manifest_file)
|
48
|
+
allow(manifest_file).to receive(:write)
|
49
|
+
|
50
|
+
allow(described_class::TEMPLATE_MANIFEST)
|
51
|
+
.to receive(:call).and_return(manifest)
|
52
|
+
|
53
|
+
described_class.render_empty_manifest(
|
54
|
+
name,
|
55
|
+
path
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'renders blank manifest' do
|
60
|
+
expect(manifest_file)
|
61
|
+
.to have_received(:write)
|
62
|
+
.with("\"#{manifest}\"")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe GeneSystem::Manifest do
|
4
|
+
describe '.new_from_file' do
|
5
|
+
let(:file_path) { 'path/to/manifest' }
|
6
|
+
let(:exist) { true }
|
7
|
+
let(:manifest_name) { 'manifest name' }
|
8
|
+
|
9
|
+
let(:gene_system_meta) do
|
10
|
+
{
|
11
|
+
'gene_system' => {
|
12
|
+
'version' => GeneSystem::VERSION
|
13
|
+
}
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:version) { '0.0.1' }
|
18
|
+
let(:platform) { 'macos' }
|
19
|
+
let(:content) do
|
20
|
+
{
|
21
|
+
'name' => manifest_name,
|
22
|
+
'version' => version,
|
23
|
+
'platform' => platform,
|
24
|
+
'metadata' => gene_system_meta,
|
25
|
+
'steps' => []
|
26
|
+
}.to_json
|
27
|
+
end
|
28
|
+
|
29
|
+
before do
|
30
|
+
allow(File).to receive(:exist?).and_return(exist)
|
31
|
+
allow(File).to receive(:read).and_return(content)
|
32
|
+
allow(Jsonnet).to receive(:evaluate).and_call_original
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when file exists' do
|
36
|
+
before do
|
37
|
+
@parsed = described_class.new_from_file(file_path)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'reads file' do
|
41
|
+
expect(File).to have_received(:read).with(file_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'parses json' do
|
45
|
+
expect(Jsonnet).to have_received(:evaluate).with(content)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns manifest' do
|
49
|
+
expect(@parsed).to be_a GeneSystem::Manifest
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'has name' do
|
53
|
+
expect(@parsed.name).to eq manifest_name
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'has version' do
|
57
|
+
expect(@parsed.version).to eq version
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'has platform' do
|
61
|
+
expect(@parsed.platform).to eq platform
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'has metadata' do
|
65
|
+
expect(@parsed.metadata).to eq gene_system_meta
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when manifest is from a later version of gene_system' do
|
70
|
+
let(:gene_system_meta) do
|
71
|
+
{
|
72
|
+
'gene_system' => {
|
73
|
+
'version' => '999.999.999'
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'raises incompatibility error' do
|
79
|
+
expect do
|
80
|
+
described_class.new_from_file(file_path)
|
81
|
+
end.to raise_error 'provided manifest is invalid or incompatible with '\
|
82
|
+
'this version of gene_system'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when gene_system metadata is missing' do
|
87
|
+
let(:gene_system_meta) { {} }
|
88
|
+
it 'raises incompatibility error' do
|
89
|
+
expect do
|
90
|
+
described_class.new_from_file(file_path)
|
91
|
+
end.to raise_error 'provided manifest is invalid or incompatible with '\
|
92
|
+
'this version of gene_system'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#name' do
|
98
|
+
let(:path) { 'path/to/manifest.json' }
|
99
|
+
|
100
|
+
let(:data) do
|
101
|
+
{
|
102
|
+
'name' => 'test_manifest',
|
103
|
+
'version' => '0.1.0',
|
104
|
+
'steps' => []
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
before do
|
109
|
+
@manifest = described_class.new(path, data)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'returns name' do
|
113
|
+
expect(@manifest.name).to eq 'test_manifest'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe '#version' do
|
118
|
+
let(:path) { 'path/to/manifest.json' }
|
119
|
+
|
120
|
+
let(:data) do
|
121
|
+
{
|
122
|
+
'name' => 'test_manifest',
|
123
|
+
'version' => '0.1.0',
|
124
|
+
'steps' => []
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
before do
|
129
|
+
@manifest = described_class.new(path, data)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'returns version' do
|
133
|
+
expect(@manifest.version).to eq '0.1.0'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '#metadata' do
|
138
|
+
let(:path) { 'path/to/manifest.json' }
|
139
|
+
let(:meta) { { 'foo' => 'bar' } }
|
140
|
+
|
141
|
+
let(:data) do
|
142
|
+
{
|
143
|
+
'name' => 'test_manifest',
|
144
|
+
'version' => '0.1.0',
|
145
|
+
'metadata' => meta,
|
146
|
+
'steps' => []
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
before do
|
151
|
+
@manifest = described_class.new(path, data)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'returns metadata' do
|
155
|
+
expect(@manifest.metadata).to eq meta
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe '#platform' do
|
160
|
+
let(:path) { 'path/to/manifest.json' }
|
161
|
+
let(:platform) { 'macos' }
|
162
|
+
|
163
|
+
let(:data) do
|
164
|
+
{
|
165
|
+
'name' => 'test_manifest',
|
166
|
+
'version' => '0.1.0',
|
167
|
+
'metadata' => { 'foo' => 'bar' },
|
168
|
+
'platform' => platform,
|
169
|
+
'steps' => []
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
before do
|
174
|
+
allow(GeneSystem::CLI).to receive(:print_warning)
|
175
|
+
@manifest = described_class.new(path, data)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'returns platform' do
|
179
|
+
expect(@manifest.platform).to eq platform
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'when platform is unrecognized' do
|
183
|
+
let(:platform) { 'yolosys' }
|
184
|
+
|
185
|
+
before do
|
186
|
+
@platform = @manifest.platform
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'returns platform' do
|
190
|
+
expect(@platform).to eq platform
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'prints unrecognised platform warning' do
|
194
|
+
expect(GeneSystem::CLI)
|
195
|
+
.to have_received(:print_warning)
|
196
|
+
.with("WARN: unrecognized platform: #{platform}")
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '#steps' do
|
202
|
+
let(:path) { 'path/to/manifest.json' }
|
203
|
+
let(:data) do
|
204
|
+
{
|
205
|
+
'name' => 'test_manifest',
|
206
|
+
'version' => '0.1.0',
|
207
|
+
'steps' => []
|
208
|
+
}
|
209
|
+
end
|
210
|
+
|
211
|
+
let(:step) { double(GeneSystem::Step) }
|
212
|
+
let(:steps) { [step, step] }
|
213
|
+
|
214
|
+
before do
|
215
|
+
@manifest = described_class.new(path, data)
|
216
|
+
@manifest.instance_variable_set(:@steps, steps)
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'returns all steps by defualt' do
|
220
|
+
expect(@manifest.steps).to eq steps
|
221
|
+
end
|
222
|
+
|
223
|
+
context 'when given a query' do
|
224
|
+
let(:target_step) { double(GeneSystem::Step) }
|
225
|
+
let(:steps) { [step, target_step, step] }
|
226
|
+
|
227
|
+
before do
|
228
|
+
query = ->(step) { step == target_step }
|
229
|
+
@manifest.instance_variable_set(:@steps, steps)
|
230
|
+
|
231
|
+
@result = @manifest.steps(query)
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'returns steps responding to query' do
|
235
|
+
expect(@result).to eq [target_step]
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe GeneSystem::Platform do
|
4
|
+
let(:is_posix) { true }
|
5
|
+
before do
|
6
|
+
allow(OS).to receive(:posix?).and_return(is_posix)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'initialize' do
|
10
|
+
it 'returns a new platform' do
|
11
|
+
@platform = described_class.new
|
12
|
+
expect(@platform).to be_a(described_class)
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'when system is not posix' do
|
16
|
+
let(:is_posix) { false }
|
17
|
+
|
18
|
+
it 'raises unsuppoorted platform RuntimeError' do
|
19
|
+
expect { described_class.new }.to raise_error(
|
20
|
+
RuntimeError,
|
21
|
+
'unsupported platform'
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#execute_commands' do
|
28
|
+
let(:cmd) { 'do something' }
|
29
|
+
let(:cmds) { [cmd, cmd] }
|
30
|
+
let(:result) { 0 }
|
31
|
+
|
32
|
+
subject { described_class.new }
|
33
|
+
|
34
|
+
before do
|
35
|
+
allow(subject).to receive(:execute_command)
|
36
|
+
.and_return(result)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'executes commands' do
|
40
|
+
subject.execute_commands(cmds)
|
41
|
+
|
42
|
+
expect(subject)
|
43
|
+
.to have_received(:execute_command)
|
44
|
+
.with(cmd, {})
|
45
|
+
.twice
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when command fails' do
|
49
|
+
let(:result) { 1 }
|
50
|
+
|
51
|
+
it 'raises command failed runtime error' do
|
52
|
+
expect { subject.execute_commands(cmds) }.to raise_error(
|
53
|
+
RuntimeError,
|
54
|
+
"command `#{cmd}` failed - returned #{result}"
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#execute_command' do
|
61
|
+
let(:cmd) { 'do something {{awesome}}' }
|
62
|
+
let(:pid) { 1234 }
|
63
|
+
let(:result) { double(exitstatus: 999) }
|
64
|
+
|
65
|
+
subject { described_class.new }
|
66
|
+
|
67
|
+
before do
|
68
|
+
allow(Process).to receive(:spawn).and_return(pid)
|
69
|
+
allow(Process).to receive(:waitpid2).and_return([pid, result])
|
70
|
+
|
71
|
+
@result = subject.execute_command(cmd, awesome: 'cool')
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'spawns process with command' do
|
75
|
+
expect(Process).to have_received(:spawn).with('do something cool')
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'waits for processs to conclude' do
|
79
|
+
expect(Process).to have_received(:waitpid2).with(pid)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns status' do
|
83
|
+
expect(@result).to eq result.exitstatus
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '#posix?' do
|
88
|
+
subject { described_class.new }
|
89
|
+
|
90
|
+
it 'returns true if system is posix' do
|
91
|
+
expect(subject.posix?).to be true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|