puppetfile-resolver 0.1.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.
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