puppetfile-resolver 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +169 -0
  4. data/lib/puppetfile-resolver.rb +7 -0
  5. data/lib/puppetfile-resolver/cache/base.rb +28 -0
  6. data/lib/puppetfile-resolver/cache/persistent.rb +50 -0
  7. data/lib/puppetfile-resolver/data/ruby_ca_certs.pem +3432 -0
  8. data/lib/puppetfile-resolver/models.rb +8 -0
  9. data/lib/puppetfile-resolver/models/missing_module_specification.rb +27 -0
  10. data/lib/puppetfile-resolver/models/module_dependency.rb +55 -0
  11. data/lib/puppetfile-resolver/models/module_specification.rb +114 -0
  12. data/lib/puppetfile-resolver/models/puppet_dependency.rb +34 -0
  13. data/lib/puppetfile-resolver/models/puppet_specification.rb +25 -0
  14. data/lib/puppetfile-resolver/models/puppetfile_dependency.rb +14 -0
  15. data/lib/puppetfile-resolver/puppetfile.rb +22 -0
  16. data/lib/puppetfile-resolver/puppetfile/base_module.rb +62 -0
  17. data/lib/puppetfile-resolver/puppetfile/document.rb +125 -0
  18. data/lib/puppetfile-resolver/puppetfile/forge_module.rb +14 -0
  19. data/lib/puppetfile-resolver/puppetfile/git_module.rb +19 -0
  20. data/lib/puppetfile-resolver/puppetfile/invalid_module.rb +16 -0
  21. data/lib/puppetfile-resolver/puppetfile/local_module.rb +14 -0
  22. data/lib/puppetfile-resolver/puppetfile/parser/errors.rb +19 -0
  23. data/lib/puppetfile-resolver/puppetfile/parser/r10k_eval.rb +133 -0
  24. data/lib/puppetfile-resolver/puppetfile/parser/r10k_eval/dsl.rb +51 -0
  25. data/lib/puppetfile-resolver/puppetfile/parser/r10k_eval/module/forge.rb +50 -0
  26. data/lib/puppetfile-resolver/puppetfile/parser/r10k_eval/module/git.rb +32 -0
  27. data/lib/puppetfile-resolver/puppetfile/parser/r10k_eval/module/invalid.rb +27 -0
  28. data/lib/puppetfile-resolver/puppetfile/parser/r10k_eval/module/local.rb +26 -0
  29. data/lib/puppetfile-resolver/puppetfile/parser/r10k_eval/module/svn.rb +30 -0
  30. data/lib/puppetfile-resolver/puppetfile/parser/r10k_eval/puppet_module.rb +36 -0
  31. data/lib/puppetfile-resolver/puppetfile/svn_module.rb +16 -0
  32. data/lib/puppetfile-resolver/puppetfile/validation_errors.rb +106 -0
  33. data/lib/puppetfile-resolver/resolution_provider.rb +182 -0
  34. data/lib/puppetfile-resolver/resolution_result.rb +30 -0
  35. data/lib/puppetfile-resolver/resolver.rb +77 -0
  36. data/lib/puppetfile-resolver/spec_searchers/common.rb +15 -0
  37. data/lib/puppetfile-resolver/spec_searchers/forge.rb +75 -0
  38. data/lib/puppetfile-resolver/spec_searchers/git.rb +64 -0
  39. data/lib/puppetfile-resolver/spec_searchers/local.rb +45 -0
  40. data/lib/puppetfile-resolver/ui/debug_ui.rb +15 -0
  41. data/lib/puppetfile-resolver/ui/null_ui.rb +20 -0
  42. data/lib/puppetfile-resolver/util.rb +23 -0
  43. data/lib/puppetfile-resolver/version.rb +5 -0
  44. data/puppetfile-cli.rb +101 -0
  45. data/spec/spec_helper.rb +48 -0
  46. data/spec/unit/puppetfile-resolver/puppetfile/document_spec.rb +316 -0
  47. data/spec/unit/puppetfile-resolver/puppetfile/parser/r10k_eval_spec.rb +460 -0
  48. data/spec/unit/puppetfile-resolver/resolver_spec.rb +421 -0
  49. metadata +124 -0
@@ -0,0 +1,460 @@
1
+ require 'spec_helper'
2
+
3
+ require 'puppetfile-resolver/puppetfile/parser/r10k_eval'
4
+
5
+ RSpec.shared_examples "a puppetfile parser with valid content" do
6
+ let(:puppetfile_content) do
7
+ <<-EOT
8
+ forge 'https://fake-forge.puppetlabs.com/'
9
+
10
+ mod 'puppetlabs-forge_fixed_ver', '1.0.0'
11
+ mod 'puppetlabs-forge_latest', :latest
12
+
13
+ mod 'git_branch',
14
+ :git => 'git@github.com:puppetlabs/puppetlabs-git_branch.git',
15
+ :branch => 'branch'
16
+
17
+ mod 'git_ref',
18
+ :git => 'git@github.com:puppetlabs/puppetlabs-git_ref.git',
19
+ :ref => 'branch'
20
+
21
+ mod 'git_commit',
22
+ :git => 'git@github.com:puppetlabs/puppetlabs-git_commit.git',
23
+ :commit => 'abc123'
24
+
25
+ mod 'git_tag',
26
+ :git => 'git@github.com:puppetlabs/puppetlabs-git_tag.git',
27
+ :tag => '0.1'
28
+
29
+ mod 'local', :local => 'some/path'
30
+
31
+ mod 'svn_min', :svn => 'some-svn-repo'
32
+ EOT
33
+ end
34
+
35
+ let(:puppetfile) { subject.parse(puppetfile_content) }
36
+
37
+ def get_module(document, title)
38
+ document.modules.find { |mod| mod.title == title }
39
+ end
40
+
41
+ it "should set the forge uri" do
42
+ expect(puppetfile.forge_uri).to eq('https://fake-forge.puppetlabs.com/')
43
+ end
44
+
45
+ it "should return the Puppetfile content" do
46
+ expect(puppetfile.content).to eq(puppetfile_content)
47
+ end
48
+
49
+ it "should detect all of the modules" do
50
+ expect(puppetfile.modules.count).to eq(8)
51
+ end
52
+
53
+ it "should not set any resolver flags" do
54
+ expect(puppetfile.modules).to all(have_attributes(:resolver_flags => []))
55
+ end
56
+
57
+ context 'with Forge modules' do
58
+ it 'should detect forge fixed version modules' do
59
+ mod = get_module(puppetfile, 'puppetlabs-forge_fixed_ver')
60
+
61
+ expect(mod.module_type).to eq(PuppetfileResolver::Puppetfile::FORGE_MODULE)
62
+ expect(mod.title).to eq('puppetlabs-forge_fixed_ver')
63
+ expect(mod.owner).to eq('puppetlabs')
64
+ expect(mod.name).to eq('forge_fixed_ver')
65
+ expect(mod.version).to eq('1.0.0')
66
+ expect(mod.location.start_line).to eq(2)
67
+ expect(mod.location.start_char).to be_nil
68
+ expect(mod.location.end_line).to eq(2)
69
+ expect(mod.location.end_char).to be_nil
70
+ end
71
+
72
+ it 'should detect forge latest version modules' do
73
+ mod = get_module(puppetfile, 'puppetlabs-forge_latest')
74
+
75
+ expect(mod.module_type).to eq(PuppetfileResolver::Puppetfile::FORGE_MODULE)
76
+ expect(mod.title).to eq('puppetlabs-forge_latest')
77
+ expect(mod.owner).to eq('puppetlabs')
78
+ expect(mod.name).to eq('forge_latest')
79
+ expect(mod.version).to eq(:latest)
80
+ expect(mod.location.start_line).to eq(3)
81
+ expect(mod.location.start_char).to be_nil
82
+ expect(mod.location.end_line).to eq(3)
83
+ expect(mod.location.end_char).to be_nil
84
+ end
85
+ end
86
+
87
+ context 'with Git modules' do
88
+ it 'should detect git branch modules' do
89
+ mod = get_module(puppetfile, 'git_branch')
90
+
91
+ expect(mod.module_type).to eq(PuppetfileResolver::Puppetfile::GIT_MODULE)
92
+ expect(mod.title).to eq('git_branch')
93
+ expect(mod.owner).to be_nil
94
+ expect(mod.name).to eq('git_branch')
95
+ expect(mod.version).to be_nil
96
+ expect(mod.remote).to eq('git@github.com:puppetlabs/puppetlabs-git_branch.git')
97
+ expect(mod.ref).to eq('branch')
98
+ expect(mod.commit).to be_nil
99
+ expect(mod.tag).to be_nil
100
+ expect(mod.location.start_line).to eq(5)
101
+ expect(mod.location.start_char).to be_nil
102
+ expect(mod.location.end_line).to eq(5)
103
+ expect(mod.location.end_char).to be_nil
104
+ end
105
+
106
+ it 'should detect git ref modules' do
107
+ mod = get_module(puppetfile, 'git_ref')
108
+
109
+ expect(mod.module_type).to eq(PuppetfileResolver::Puppetfile::GIT_MODULE)
110
+ expect(mod.title).to eq('git_ref')
111
+ expect(mod.owner).to be_nil
112
+ expect(mod.name).to eq('git_ref')
113
+ expect(mod.version).to be_nil
114
+ expect(mod.remote).to eq('git@github.com:puppetlabs/puppetlabs-git_ref.git')
115
+ expect(mod.ref).to eq('branch')
116
+ expect(mod.commit).to be_nil
117
+ expect(mod.tag).to be_nil
118
+ expect(mod.location.start_line).to eq(9)
119
+ expect(mod.location.start_char).to be_nil
120
+ expect(mod.location.end_line).to eq(9)
121
+ expect(mod.location.end_char).to be_nil
122
+ end
123
+
124
+ it 'should detect git commit modules' do
125
+ mod = get_module(puppetfile, 'git_commit')
126
+
127
+ expect(mod.module_type).to eq(PuppetfileResolver::Puppetfile::GIT_MODULE)
128
+ expect(mod.title).to eq('git_commit')
129
+ expect(mod.owner).to be_nil
130
+ expect(mod.name).to eq('git_commit')
131
+ expect(mod.version).to be_nil
132
+ expect(mod.remote).to eq('git@github.com:puppetlabs/puppetlabs-git_commit.git')
133
+ expect(mod.ref).to be_nil
134
+ expect(mod.commit).to eq('abc123')
135
+ expect(mod.tag).to be_nil
136
+ expect(mod.location.start_line).to eq(13)
137
+ expect(mod.location.start_char).to be_nil
138
+ expect(mod.location.end_line).to eq(13)
139
+ expect(mod.location.end_char).to be_nil
140
+ end
141
+
142
+ it 'should detect git tag modules' do
143
+ mod = get_module(puppetfile, 'git_tag')
144
+
145
+ expect(mod.module_type).to eq(PuppetfileResolver::Puppetfile::GIT_MODULE)
146
+ expect(mod.title).to eq('git_tag')
147
+ expect(mod.owner).to be_nil
148
+ expect(mod.name).to eq('git_tag')
149
+ expect(mod.version).to be_nil
150
+ expect(mod.remote).to eq('git@github.com:puppetlabs/puppetlabs-git_tag.git')
151
+ expect(mod.ref).to be_nil
152
+ expect(mod.commit).to be_nil
153
+ expect(mod.tag).to eq('0.1')
154
+ expect(mod.location.start_line).to eq(17)
155
+ expect(mod.location.start_char).to be_nil
156
+ expect(mod.location.end_line).to eq(17)
157
+ expect(mod.location.end_char).to be_nil
158
+ end
159
+ end
160
+
161
+ context 'with Local modules' do
162
+ it 'should detect local modules' do
163
+ mod = get_module(puppetfile, 'local')
164
+
165
+ expect(mod.module_type).to eq(PuppetfileResolver::Puppetfile::LOCAL_MODULE)
166
+ expect(mod.title).to eq('local')
167
+ expect(mod.owner).to be_nil
168
+ expect(mod.name).to eq('local')
169
+ expect(mod.version).to be_nil
170
+ expect(mod.location.start_line).to eq(21)
171
+ expect(mod.location.start_char).to be_nil
172
+ expect(mod.location.end_line).to eq(21)
173
+ expect(mod.location.end_char).to be_nil
174
+ end
175
+ end
176
+
177
+ context 'with SVN modules' do
178
+ it 'should detect svn modules' do
179
+ mod = get_module(puppetfile, 'svn_min')
180
+
181
+ expect(mod.module_type).to eq(PuppetfileResolver::Puppetfile::SVN_MODULE)
182
+ expect(mod.title).to eq('svn_min')
183
+ expect(mod.owner).to be_nil
184
+ expect(mod.name).to eq('svn_min')
185
+ expect(mod.version).to be_nil
186
+ expect(mod.remote).to eq('some-svn-repo')
187
+ expect(mod.location.start_line).to eq(23)
188
+ expect(mod.location.start_char).to be_nil
189
+ expect(mod.location.end_line).to eq(23)
190
+ expect(mod.location.end_char).to be_nil
191
+ end
192
+ end
193
+ end
194
+
195
+ RSpec.shared_examples "a puppetfile parser with magic comments" do
196
+ def get_module(document, title)
197
+ document.modules.find { |mod| mod.title == title }
198
+ end
199
+
200
+ context 'with differnt types of magic comments' do
201
+ let(:flag_name) { 'Dependency/Puppet' }
202
+ let(:flag) { PuppetfileResolver::Puppetfile::DISABLE_PUPPET_DEPENDENCY_FLAG }
203
+ let(:flag_name2) { 'Dependency/All' }
204
+ let(:flag2) { PuppetfileResolver::Puppetfile::DISABLE_ALL_DEPENDENCIES_FLAG }
205
+
206
+ let(:puppetfile_content) do
207
+ <<-EOT
208
+ forge 'https://fake-forge.puppetlabs.com/'
209
+
210
+ mod 'puppetlabs-inline', :latest # resolver:disable #{flag_name}
211
+
212
+ # resolver:disable #{flag_name}
213
+ mod 'puppetlabs-block', :latest
214
+ # resolver:enable #{flag_name}
215
+
216
+ # resolver:disable #{flag_name}
217
+ # resolver:disable #{flag_name}
218
+ mod 'puppetlabs-overlap1', :latest
219
+ # resolver:enable #{flag_name}
220
+ # resolver:enable #{flag_name}
221
+
222
+ # resolver:disable #{flag_name}
223
+ mod 'puppetlabs-overlap2',
224
+ :git => 'git@github.com:puppetlabs/puppetlabs-git_branch.git',
225
+ # resolver:enable #{flag_name}
226
+ :branch => 'branch'
227
+
228
+ mod 'puppetlabs-overlap3',
229
+ # resolver:disable #{flag_name}
230
+ :git => 'git@github.com:puppetlabs/puppetlabs-git_branch.git',
231
+ :branch => 'branch'
232
+ # resolver:enable #{flag_name}
233
+
234
+
235
+ mod 'puppetlabs-nomagic', :latest
236
+ EOT
237
+ end
238
+ let(:puppetfile) { subject.parse(puppetfile_content) }
239
+
240
+ it "should freeze the resolver flags" do
241
+ puppetfile.modules.each do |mod|
242
+ expect(mod.resolver_flags).to be_frozen
243
+ end
244
+ end
245
+
246
+ it 'should add the flag with inline magic comments' do
247
+ mod = get_module(puppetfile, 'puppetlabs-inline')
248
+ expect(mod.resolver_flags).to eq([flag])
249
+ end
250
+
251
+ it 'should add the flag with magic comment ranges' do
252
+ mod = get_module(puppetfile, 'puppetlabs-block')
253
+ expect(mod.resolver_flags).to eq([flag])
254
+ end
255
+
256
+ it 'should ignore overlapping ranges and only add the flag once' do
257
+ mod = get_module(puppetfile, 'puppetlabs-overlap1')
258
+ expect(mod.resolver_flags).to eq([flag])
259
+ end
260
+
261
+ it 'should add the flag with magic comment range if it spans the beginning of a multiline module definition' do
262
+ mod = get_module(puppetfile, 'puppetlabs-overlap2')
263
+ expect(mod.resolver_flags).to eq([flag])
264
+ end
265
+
266
+ it 'should add the flag with magic comment range if it spans the end of multiline module definition' do
267
+ pending('The Ruby Eval method can\'t detect module definition spans and only looks at the first line')
268
+ mod = get_module(puppetfile, 'puppetlabs-overlap3')
269
+ expect(mod.resolver_flags).to eq([flag])
270
+ end
271
+
272
+ it 'should not add flags to unaffected modules' do
273
+ mod = get_module(puppetfile, 'puppetlabs-nomagic')
274
+ expect(mod.resolver_flags).to eq([])
275
+ end
276
+
277
+ context 'with a flag that is never re-enabled' do
278
+ let(:puppetfile_content) do
279
+ <<-EOT
280
+ forge 'https://fake-forge.puppetlabs.com/'
281
+
282
+ mod 'puppetlabs-nomagic', :latest
283
+
284
+ # resolver:disable #{flag_name}
285
+
286
+ mod 'puppetlabs-block', :latest
287
+ EOT
288
+ end
289
+
290
+ it 'should not add flags to unaffected modules' do
291
+ mod = get_module(puppetfile, 'puppetlabs-nomagic')
292
+ expect(mod.resolver_flags).to eq([])
293
+ end
294
+
295
+ it 'should add the flag to the subsequent modules' do
296
+ mod = get_module(puppetfile, 'puppetlabs-block')
297
+ expect(mod.resolver_flags).to eq([flag])
298
+ end
299
+ end
300
+
301
+ context 'with a flag that is specified more than once' do
302
+ let(:puppetfile_content) do
303
+ <<-EOT
304
+ forge 'https://fake-forge.puppetlabs.com/'
305
+
306
+ mod 'puppetlabs-block', :latest # resolver:disable #{flag_name},#{flag_name},#{flag_name},#{flag_name} Some reason
307
+ EOT
308
+ end
309
+
310
+ it 'should add the flag only once' do
311
+ mod = get_module(puppetfile, 'puppetlabs-block')
312
+ expect(mod.resolver_flags).to eq([flag])
313
+ end
314
+ end
315
+
316
+ context 'with multiple valid flags' do
317
+ let(:puppetfile_content) do
318
+ <<-EOT
319
+ forge 'https://fake-forge.puppetlabs.com/'
320
+
321
+ mod 'puppetlabs-block', :latest # resolver:disable #{flag_name},#{flag_name2} Another good reason reason
322
+ EOT
323
+ end
324
+
325
+ it 'should add the flags' do
326
+ mod = get_module(puppetfile, 'puppetlabs-block')
327
+ expect(mod.resolver_flags).to eq([flag, flag2])
328
+ end
329
+ end
330
+
331
+ context 'with invalid flags' do
332
+ let(:puppetfile_content) do
333
+ <<-EOT
334
+ forge 'https://fake-forge.puppetlabs.com/'
335
+
336
+ mod 'puppetlabs-block', :latest # resolver:disable #{flag_name},missing,foo,bar baz Another good reason reason
337
+ EOT
338
+ end
339
+
340
+ it 'should add the valid flags and ignore the invalid flags' do
341
+ mod = get_module(puppetfile, 'puppetlabs-block')
342
+ expect(mod.resolver_flags).to eq([flag])
343
+ end
344
+ end
345
+ end
346
+
347
+ context 'with all available flags' do
348
+ let(:puppetfile_content) do
349
+ <<-EOT
350
+ forge 'https://fake-forge.puppetlabs.com/'
351
+
352
+ mod 'puppetlabs-magic1', :latest # resolver:disable Dependency/Puppet
353
+ mod 'puppetlabs-magic2', :latest # resolver:disable Dependency/all
354
+ EOT
355
+ end
356
+ let(:puppetfile) { subject.parse(puppetfile_content) }
357
+
358
+ it 'should add the DISABLE_PUPPET_DEPENDENCY_FLAG flag for Dependency/Puppet' do
359
+ mod = get_module(puppetfile, 'puppetlabs-magic1')
360
+ expect(mod.resolver_flags).to eq([PuppetfileResolver::Puppetfile::DISABLE_PUPPET_DEPENDENCY_FLAG])
361
+ end
362
+
363
+ it 'should add the DISABLE_ALL_DEPENDENCIES_FLAG flag for Dependency/All' do
364
+ mod = get_module(puppetfile, 'puppetlabs-magic2')
365
+ expect(mod.resolver_flags).to eq([PuppetfileResolver::Puppetfile::DISABLE_ALL_DEPENDENCIES_FLAG])
366
+ end
367
+ end
368
+ end
369
+
370
+ RSpec.shared_examples "a puppetfile parser with invalid content" do
371
+ let(:puppetfile_content) do
372
+ <<-EOT
373
+ forge 'https://fake-forge.puppetlabs.com/'
374
+
375
+ mod 'puppetlabs-bad_version', 'b.c.d'
376
+ mod 'puppetlabs-missing_version'
377
+
378
+ mod 'bad_args',
379
+ :gitx => 'git@github.com:puppetlabs/puppetlabs-git_ref.git'
380
+
381
+ EOT
382
+ end
383
+ let(:puppetfile) { subject.parse(puppetfile_content) }
384
+
385
+ def get_module(document, title)
386
+ document.modules.find { |mod| mod.title == title }
387
+ end
388
+
389
+ it "should detect all of the invalid modules" do
390
+ expect(puppetfile.modules.count).to eq(3)
391
+
392
+ puppetfile.modules.each do |mod|
393
+ expect(mod).to have_attributes(:module_type => PuppetfileResolver::Puppetfile::INVALID_MODULE)
394
+ expect(mod.reason).to_not be_nil
395
+ end
396
+ end
397
+ end
398
+
399
+ RSpec.shared_examples "a puppetfile parser which raises" do
400
+ [
401
+ {
402
+ name: 'with an unknown method',
403
+ content: "mod_abc 'puppetlabs-forge_fixed_ver', '1.0.0'\n",
404
+ },
405
+ {
406
+ name: 'with a bad forge module name',
407
+ content: "mod 'bad\nname', '1.0.0'\n",
408
+ },
409
+ {
410
+ name: 'with a syntax error',
411
+ content: "} # syntax\n",
412
+ },
413
+ {
414
+ name: 'with a syntax error in the middle of the line',
415
+ content: "forge 'something' } # syntax\n",
416
+ },
417
+ {
418
+ name: 'with an unknown require',
419
+ content: "require 'not_loadable' # I am a load error\n",
420
+ },
421
+ {
422
+ name: 'with a runtime error',
423
+ content: "raise 'A Mock Runtime Error'\n",
424
+ },
425
+ ].each_with_index do |testcase, testcase_index|
426
+ context "Given a puppetfile with #{testcase[:name]}" do
427
+ let(:puppetfile_content) do
428
+ # Add interesting things to the puppetfile content, so it's not just a single line
429
+ "# Padding\n" * testcase_index + testcase[:content] + "# Padding\n" * testcase_index
430
+ end
431
+ let(:expected_error_line) { testcase_index }
432
+
433
+ it "should raise a parsing error" do
434
+ expect{ subject.parse(puppetfile_content) }.to raise_error(PuppetfileResolver::Puppetfile::Parser::ParserError)
435
+ end
436
+
437
+ it "should locate the error in the puppetfile" do
438
+ begin
439
+ subject.parse(puppetfile_content)
440
+ rescue PuppetfileResolver::Puppetfile::Parser::ParserError => e
441
+ expect(e.location.start_line).to eq(expected_error_line)
442
+ expect(e.location.end_line).to eq(expected_error_line)
443
+ # TODO: What about character position?
444
+ end
445
+ end
446
+ end
447
+ end
448
+ end
449
+
450
+ describe PuppetfileResolver::Puppetfile::Parser::R10KEval do
451
+ let(:subject) { PuppetfileResolver::Puppetfile::Parser::R10KEval }
452
+
453
+ it_behaves_like "a puppetfile parser with valid content"
454
+
455
+ it_behaves_like 'a puppetfile parser with magic comments'
456
+
457
+ it_behaves_like "a puppetfile parser with invalid content"
458
+
459
+ it_behaves_like "a puppetfile parser which raises"
460
+ end