r10k 3.9.3 → 3.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec_tests.yml +1 -1
  3. data/CHANGELOG.mkd +9 -0
  4. data/doc/dynamic-environments/configuration.mkd +7 -0
  5. data/integration/Rakefile +1 -1
  6. data/integration/tests/user_scenario/basic_workflow/negative/neg_specify_deleted_forge_module.rb +3 -9
  7. data/integration/tests/user_scenario/basic_workflow/single_env_purge_unmanaged_modules.rb +8 -14
  8. data/lib/r10k/action/deploy/environment.rb +3 -0
  9. data/lib/r10k/action/runner.rb +11 -6
  10. data/lib/r10k/git/cache.rb +1 -1
  11. data/lib/r10k/initializers.rb +7 -0
  12. data/lib/r10k/module/forge.rb +5 -1
  13. data/lib/r10k/module_loader/puppetfile.rb +195 -0
  14. data/lib/r10k/module_loader/puppetfile/dsl.rb +37 -0
  15. data/lib/r10k/puppetfile.rb +77 -153
  16. data/lib/r10k/settings.rb +3 -0
  17. data/lib/r10k/source/base.rb +10 -0
  18. data/lib/r10k/source/git.rb +5 -0
  19. data/lib/r10k/source/svn.rb +4 -0
  20. data/lib/r10k/util/purgeable.rb +70 -8
  21. data/lib/r10k/version.rb +1 -1
  22. data/locales/r10k.pot +37 -33
  23. data/spec/fixtures/unit/action/r10k_forge_auth.yaml +4 -0
  24. data/spec/fixtures/unit/action/r10k_forge_auth_no_url.yaml +3 -0
  25. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/managed_subdir_2/ignored_1 +0 -0
  26. data/spec/fixtures/unit/util/purgeable/managed_two/.hidden/unmanaged_3 +0 -0
  27. data/spec/unit/action/deploy/environment_spec.rb +9 -0
  28. data/spec/unit/action/deploy/module_spec.rb +38 -14
  29. data/spec/unit/action/runner_spec.rb +49 -25
  30. data/spec/unit/git/cache_spec.rb +14 -0
  31. data/spec/unit/module/forge_spec.rb +8 -1
  32. data/spec/unit/module_loader/puppetfile_spec.rb +330 -0
  33. data/spec/unit/puppetfile_spec.rb +99 -193
  34. data/spec/unit/settings_spec.rb +6 -2
  35. data/spec/unit/util/purgeable_spec.rb +38 -6
  36. metadata +10 -3
@@ -0,0 +1,330 @@
1
+ require 'spec_helper'
2
+ require 'r10k/module_loader/puppetfile'
3
+
4
+ describe R10K::ModuleLoader::Puppetfile do
5
+ describe 'initial parameters' do
6
+ describe 'honor' do
7
+ let(:options) do
8
+ {
9
+ basedir: '/test/basedir/env',
10
+ forge: 'localforge.internal.corp',
11
+ overrides: { modules: { deploy_modules: true } },
12
+ environment: R10K::Environment::Git.new('env',
13
+ '/test/basedir/',
14
+ 'env',
15
+ { remote: 'git://foo/remote',
16
+ ref: 'env' })
17
+ }
18
+ end
19
+
20
+ subject { R10K::ModuleLoader::Puppetfile.new(**options) }
21
+
22
+ describe 'the moduledir' do
23
+ it 'respects absolute paths' do
24
+ absolute_options = options.merge({moduledir: '/opt/puppetlabs/special/modules'})
25
+ puppetfile = R10K::ModuleLoader::Puppetfile.new(**absolute_options)
26
+ expect(puppetfile.instance_variable_get(:@moduledir)).to eq('/opt/puppetlabs/special/modules')
27
+ end
28
+
29
+ it 'roots the moduledir in the basepath if a relative path is specified' do
30
+ relative_options = options.merge({moduledir: 'my/special/modules'})
31
+ puppetfile = R10K::ModuleLoader::Puppetfile.new(**relative_options)
32
+ expect(puppetfile.instance_variable_get(:@moduledir)).to eq('/test/basedir/env/my/special/modules')
33
+ end
34
+ end
35
+
36
+ describe 'the Puppetfile' do
37
+ it 'respects absolute paths' do
38
+ absolute_options = options.merge({puppetfile: '/opt/puppetlabs/special/Puppetfile'})
39
+ puppetfile = R10K::ModuleLoader::Puppetfile.new(**absolute_options)
40
+ expect(puppetfile.instance_variable_get(:@puppetfile)).to eq('/opt/puppetlabs/special/Puppetfile')
41
+ end
42
+
43
+ it 'roots the Puppetfile in the basepath if a relative path is specified' do
44
+ relative_options = options.merge({puppetfile: 'Puppetfile.global'})
45
+ puppetfile = R10K::ModuleLoader::Puppetfile.new(**relative_options)
46
+ expect(puppetfile.instance_variable_get(:@puppetfile)).to eq('/test/basedir/env/Puppetfile.global')
47
+ end
48
+ end
49
+
50
+ it 'the forge' do
51
+ expect(subject.instance_variable_get(:@forge)).to eq('localforge.internal.corp')
52
+ end
53
+
54
+ it 'the overrides' do
55
+ expect(subject.instance_variable_get(:@overrides)).to eq({ modules: { deploy_modules: true }})
56
+ end
57
+
58
+ it 'the environment' do
59
+ expect(subject.instance_variable_get(:@environment).name).to eq('env')
60
+ end
61
+ end
62
+
63
+ describe 'sane defaults' do
64
+ subject { R10K::ModuleLoader::Puppetfile.new(basedir: '/test/basedir') }
65
+
66
+ it 'has a moduledir rooted in the basedir' do
67
+ expect(subject.instance_variable_get(:@moduledir)).to eq('/test/basedir/modules')
68
+ end
69
+
70
+ it 'has a Puppetfile rooted in the basedir' do
71
+ expect(subject.instance_variable_get(:@puppetfile)).to eq('/test/basedir/Puppetfile')
72
+ end
73
+
74
+ it 'uses the public forge' do
75
+ expect(subject.instance_variable_get(:@forge)).to eq('forgeapi.puppetlabs.com')
76
+ end
77
+
78
+ it 'creates an empty overrides' do
79
+ expect(subject.instance_variable_get(:@overrides)).to eq({})
80
+ end
81
+
82
+ it 'does not require an environment' do
83
+ expect(subject.instance_variable_get(:@environment)).to eq(nil)
84
+ end
85
+ end
86
+ end
87
+
88
+ describe 'adding modules' do
89
+ let(:basedir) { '/test/basedir' }
90
+
91
+ subject { R10K::ModuleLoader::Puppetfile.new(basedir: basedir) }
92
+
93
+ it 'should transform Forge modules with a string arg to have a version key' do
94
+ expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
95
+
96
+ expect { subject.add_module('puppet/test_module', '1.2.3') }.to change { subject.modules }
97
+ expect(subject.modules.collect(&:name)).to include('test_module')
98
+ end
99
+
100
+ it 'should not accept Forge modules with a version comparison' do
101
+ expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '< 1.2.0'), anything).and_call_original
102
+
103
+ expect {
104
+ subject.add_module('puppet/test_module', '< 1.2.0')
105
+ }.to raise_error(RuntimeError, /module puppet\/test_module.*doesn't have an implementation/i)
106
+
107
+ expect(subject.modules.collect(&:name)).not_to include('test_module')
108
+ end
109
+
110
+ it 'should accept non-Forge modules with a hash arg' do
111
+ module_opts = { git: 'git@example.com:puppet/test_module.git' }
112
+
113
+ expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, module_opts, anything).and_call_original
114
+
115
+ expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
116
+ expect(subject.modules.collect(&:name)).to include('test_module')
117
+ end
118
+
119
+ it 'should accept non-Forge modules with a valid relative :install_path option' do
120
+ module_opts = {
121
+ install_path: 'vendor',
122
+ git: 'git@example.com:puppet/test_module.git',
123
+ }
124
+
125
+ expect(R10K::Module).to receive(:new).with('puppet/test_module', File.join(basedir, 'vendor'), module_opts, anything).and_call_original
126
+
127
+ expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
128
+ expect(subject.modules.collect(&:name)).to include('test_module')
129
+ end
130
+
131
+ it 'should accept non-Forge modules with a valid absolute :install_path option' do
132
+ install_path = File.join(basedir, 'vendor')
133
+
134
+ module_opts = {
135
+ install_path: install_path,
136
+ git: 'git@example.com:puppet/test_module.git',
137
+ }
138
+
139
+ expect(R10K::Module).to receive(:new).with('puppet/test_module', install_path, module_opts, anything).and_call_original
140
+
141
+ expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
142
+ expect(subject.modules.collect(&:name)).to include('test_module')
143
+ end
144
+
145
+ it 'should reject non-Forge modules with an invalid relative :install_path option' do
146
+ module_opts = {
147
+ install_path: '../../vendor',
148
+ git: 'git@example.com:puppet/test_module.git',
149
+ }
150
+
151
+ expect { subject.add_module('puppet/test_module', module_opts) }.to raise_error(R10K::Error, /cannot manage content.*is not within/i).and not_change { subject.modules }
152
+ end
153
+
154
+ it 'should reject non-Forge modules with an invalid absolute :install_path option' do
155
+ module_opts = {
156
+ install_path: '/tmp/mydata/vendor',
157
+ git: 'git@example.com:puppet/test_module.git',
158
+ }
159
+
160
+ expect { subject.add_module('puppet/test_module', module_opts) }.to raise_error(R10K::Error, /cannot manage content.*is not within/i).and not_change { subject.modules }
161
+ end
162
+
163
+ it 'should disable and not add modules that conflict with the environment' do
164
+ env = instance_double('R10K::Environment::Base')
165
+ mod = instance_double('R10K::Module::Base', name: 'conflict', origin: :puppetfile, 'origin=': nil)
166
+ loader = R10K::ModuleLoader::Puppetfile.new(basedir: basedir, environment: env)
167
+ allow(env).to receive(:'module_conflicts?').with(mod).and_return(true)
168
+
169
+ expect(R10K::Module).to receive(:new).with('conflict', anything, anything, anything).and_return(mod)
170
+ expect { loader.add_module('conflict', {}) }.not_to change { loader.modules }
171
+ end
172
+ end
173
+
174
+ describe '#purge_exclusions' do
175
+ let(:managed_dirs) { ['dir1', 'dir2'] }
176
+ subject { R10K::ModuleLoader::Puppetfile.new(basedir: '/test/basedir') }
177
+
178
+ it 'includes managed_directories' do
179
+ expect(subject.send(:determine_purge_exclusions, managed_dirs)).to match_array(managed_dirs)
180
+ end
181
+
182
+ context 'when belonging to an environment' do
183
+ let(:env_contents) { ['env1', 'env2' ] }
184
+ let(:env) { double(:environment, desired_contents: env_contents) }
185
+
186
+ subject { R10K::ModuleLoader::Puppetfile.new(basedir: '/test/basedir', environment: env) }
187
+
188
+ it "includes environment's desired_contents" do
189
+ expect(subject.send(:determine_purge_exclusions, managed_dirs)).to match_array(managed_dirs + env_contents)
190
+ end
191
+ end
192
+ end
193
+
194
+ describe '#managed_directories' do
195
+
196
+ let(:basedir) { '/test/basedir' }
197
+ subject { R10K::ModuleLoader::Puppetfile.new(basedir: basedir) }
198
+
199
+ before do
200
+ allow(subject).to receive(:puppetfile_content).and_return('')
201
+ end
202
+
203
+ it 'returns an array of paths that #purge! will operate within' do
204
+ expect(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
205
+ subject.add_module('puppet/test_module', '1.2.3')
206
+ subject.load
207
+
208
+ expect(subject.modules.length).to be 1
209
+ expect(subject.managed_directories).to match_array([subject.moduledir])
210
+ end
211
+
212
+ context "with a module with install_path == ''" do
213
+ it "basedir isn't in the list of paths to purge" do
214
+ module_opts = { install_path: '', git: 'git@example.com:puppet/test_module.git' }
215
+
216
+ expect(R10K::Module).to receive(:new).with('puppet/test_module', basedir, module_opts, anything).and_call_original
217
+ subject.add_module('puppet/test_module', module_opts)
218
+ subject.load
219
+
220
+ expect(subject.modules.length).to be 1
221
+ expect(subject.managed_directories).to be_empty
222
+ end
223
+ end
224
+ end
225
+
226
+ describe 'evaluating a Puppetfile' do
227
+ def expect_wrapped_error(error, pf_path, error_type)
228
+ expect(error).to be_a_kind_of(R10K::Error)
229
+ expect(error.message).to eq("Failed to evaluate #{pf_path}")
230
+ expect(error.original).to be_a_kind_of(error_type)
231
+ end
232
+
233
+ subject { described_class.new(basedir: @path) }
234
+
235
+ it 'wraps and re-raises syntax errors' do
236
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'invalid-syntax')
237
+ pf_path = File.join(@path, 'Puppetfile')
238
+ expect {
239
+ subject.load
240
+ }.to raise_error do |e|
241
+ expect_wrapped_error(e, pf_path, SyntaxError)
242
+ end
243
+ end
244
+
245
+ it 'wraps and re-raises load errors' do
246
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'load-error')
247
+ pf_path = File.join(@path, 'Puppetfile')
248
+ expect {
249
+ subject.load
250
+ }.to raise_error do |e|
251
+ expect_wrapped_error(e, pf_path, LoadError)
252
+ end
253
+ end
254
+
255
+ it 'wraps and re-raises argument errors' do
256
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'argument-error')
257
+ pf_path = File.join(@path, 'Puppetfile')
258
+ expect {
259
+ subject.load
260
+ }.to raise_error do |e|
261
+ expect_wrapped_error(e, pf_path, ArgumentError)
262
+ end
263
+ end
264
+
265
+ it 'rejects Puppetfiles with duplicate module names' do
266
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'duplicate-module-error')
267
+ pf_path = File.join(@path, 'Puppetfile')
268
+ expect {
269
+ subject.load
270
+ }.to raise_error(R10K::Error, /Puppetfiles cannot contain duplicate module names/i)
271
+ end
272
+
273
+ it 'wraps and re-raises name errors' do
274
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'name-error')
275
+ pf_path = File.join(@path, 'Puppetfile')
276
+ expect {
277
+ subject.load
278
+ }.to raise_error do |e|
279
+ expect_wrapped_error(e, pf_path, NameError)
280
+ end
281
+ end
282
+
283
+ it 'accepts a forge module with a version' do
284
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
285
+ pf_path = File.join(@path, 'Puppetfile')
286
+ expect { subject.load }.not_to raise_error
287
+ end
288
+
289
+ describe 'setting a custom moduledir' do
290
+ it 'allows setting an absolute moduledir' do
291
+ @path = '/fake/basedir'
292
+ allow(subject).to receive(:puppetfile_content).and_return('moduledir "/fake/moduledir"')
293
+ subject.load
294
+ expect(subject.instance_variable_get(:@moduledir)).to eq('/fake/moduledir')
295
+ end
296
+
297
+ it 'roots relative moduledirs in the basedir' do
298
+ @path = '/fake/basedir'
299
+ allow(subject).to receive(:puppetfile_content).and_return('moduledir "my/moduledir"')
300
+ subject.load
301
+ expect(subject.instance_variable_get(:@moduledir)).to eq(File.join(@path, 'my/moduledir'))
302
+ end
303
+ end
304
+
305
+ it 'accepts a forge module without a version' do
306
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-without-version')
307
+ pf_path = File.join(@path, 'Puppetfile')
308
+ expect { subject.load }.not_to raise_error
309
+ end
310
+
311
+ it 'creates a git module and applies the default branch specified in the Puppetfile' do
312
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'default-branch-override')
313
+ pf_path = File.join(@path, 'Puppetfile')
314
+ expect { subject.load }.not_to raise_error
315
+ git_module = subject.modules[0]
316
+ expect(git_module.default_ref).to eq 'here_lies_the_default_branch'
317
+ end
318
+
319
+ it 'creates a git module and applies the provided default_branch_override' do
320
+ @path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'default-branch-override')
321
+ pf_path = File.join(@path, 'Puppetfile')
322
+ default_branch_override = 'default_branch_override_name'
323
+ subject.default_branch_override = default_branch_override
324
+ expect { subject.load }.not_to raise_error
325
+ git_module = subject.modules[0]
326
+ expect(git_module.default_override_ref).to eq default_branch_override
327
+ expect(git_module.default_ref).to eq 'here_lies_the_default_branch'
328
+ end
329
+ end
330
+ end
@@ -67,230 +67,136 @@ describe R10K::Puppetfile do
67
67
  end
68
68
  end
69
69
 
70
- describe "adding modules" do
71
- it "should transform Forge modules with a string arg to have a version key" do
72
- allow(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
70
+ describe "loading a Puppetfile" do
71
+ context 'using load' do
72
+ it "returns the loaded content" do
73
+ path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
74
+ subject = described_class.new(path, {})
73
75
 
74
- expect { subject.add_module('puppet/test_module', '1.2.3') }.to change { subject.modules }
75
- expect(subject.modules.collect(&:name)).to include('test_module')
76
- end
77
-
78
- it "should not accept Forge modules with a version comparison" do
79
- allow(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '< 1.2.0'), anything).and_call_original
80
-
81
- expect {
82
- subject.add_module('puppet/test_module', '< 1.2.0')
83
- }.to raise_error(RuntimeError, /module puppet\/test_module.*doesn't have an implementation/i)
84
-
85
- expect(subject.modules.collect(&:name)).not_to include('test_module')
86
- end
87
-
88
- it "should accept non-Forge modules with a hash arg" do
89
- module_opts = { git: 'git@example.com:puppet/test_module.git' }
90
-
91
- allow(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, module_opts, anything).and_call_original
92
-
93
- expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
94
- expect(subject.modules.collect(&:name)).to include('test_module')
95
- end
96
-
97
- it "should accept non-Forge modules with a valid relative :install_path option" do
98
- module_opts = {
99
- install_path: 'vendor',
100
- git: 'git@example.com:puppet/test_module.git',
101
- }
102
-
103
- allow(R10K::Module).to receive(:new).with('puppet/test_module', File.join(subject.basedir, 'vendor'), module_opts, anything).and_call_original
104
-
105
- expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
106
- expect(subject.modules.collect(&:name)).to include('test_module')
107
- end
76
+ loaded_content = subject.load
77
+ expect(loaded_content).to be_an_instance_of(Hash)
108
78
 
109
- it "should accept non-Forge modules with a valid absolute :install_path option" do
110
- install_path = File.join(subject.basedir, 'vendor')
111
-
112
- module_opts = {
113
- install_path: install_path,
114
- git: 'git@example.com:puppet/test_module.git',
115
- }
116
-
117
- allow(R10K::Module).to receive(:new).with('puppet/test_module', install_path, module_opts, anything).and_call_original
118
-
119
- expect { subject.add_module('puppet/test_module', module_opts) }.to change { subject.modules }
120
- expect(subject.modules.collect(&:name)).to include('test_module')
121
- end
122
-
123
- it "should reject non-Forge modules with an invalid relative :install_path option" do
124
- module_opts = {
125
- install_path: '../../vendor',
126
- git: 'git@example.com:puppet/test_module.git',
127
- }
128
-
129
- allow(R10K::Module).to receive(:new).with('puppet/test_module', File.join(subject.basedir, 'vendor'), module_opts, anything).and_call_original
130
-
131
- expect { subject.add_module('puppet/test_module', module_opts) }.to raise_error(R10K::Error, /cannot manage content.*is not within/i).and not_change { subject.modules }
132
- end
133
-
134
- it "should reject non-Forge modules with an invalid absolute :install_path option" do
135
- module_opts = {
136
- install_path: '/tmp/mydata/vendor',
137
- git: 'git@example.com:puppet/test_module.git',
138
- }
139
-
140
- allow(R10K::Module).to receive(:new).with('puppet/test_module', File.join(subject.basedir, 'vendor'), module_opts, anything).and_call_original
79
+ has_some_data = loaded_content.values.none?(&:empty?)
80
+ expect(has_some_data).to be true
81
+ end
141
82
 
142
- expect { subject.add_module('puppet/test_module', module_opts) }.to raise_error(R10K::Error, /cannot manage content.*is not within/i).and not_change { subject.modules }
143
- end
83
+ it "is idempotent" do
84
+ path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
85
+ subject = described_class.new(path, {})
144
86
 
145
- it "should disable and not add modules that conflict with the environment" do
146
- env = instance_double('R10K::Environment::Base')
147
- mod = instance_double('R10K::Module::Base', name: 'conflict', origin: :puppetfile)
148
- allow(mod).to receive(:origin=).and_return(nil)
149
- allow(subject).to receive(:environment).and_return(env)
150
- allow(env).to receive(:'module_conflicts?').with(mod).and_return(true)
87
+ expect(subject.loader).to receive(:load).and_call_original.once
151
88
 
152
- allow(R10K::Module).to receive(:new).with('test', anything, anything, anything).and_return(mod)
153
- expect { subject.add_module('test', {}) }.not_to change { subject.modules }
154
- end
155
- end
89
+ loaded_content1 = subject.load
90
+ expect(subject.loaded?).to be true
91
+ loaded_content2 = subject.load
156
92
 
157
- describe "#purge_exclusions" do
158
- let(:managed_dirs) { ['dir1', 'dir2'] }
93
+ expect(loaded_content2).to eq(loaded_content1)
94
+ end
159
95
 
160
- before(:each) do
161
- allow(subject).to receive(:managed_directories).and_return(managed_dirs)
96
+ it "returns nil if Puppetfile doesn't exist" do
97
+ path = '/rando/path/that/wont/exist'
98
+ subject = described_class.new(path, {})
99
+ expect(subject.load).to eq nil
100
+ end
162
101
  end
163
102
 
164
- it "includes managed_directories" do
165
- expect(subject.purge_exclusions).to match_array(managed_dirs)
166
- end
103
+ context 'using load!' do
104
+ it "returns the loaded content" do
105
+ path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
106
+ subject = described_class.new(path, {})
167
107
 
168
- context "when belonging to an environment" do
169
- let(:env_contents) { ['env1', 'env2' ] }
108
+ loaded_content = subject.load!
109
+ expect(loaded_content).to be_an_instance_of(Hash)
170
110
 
171
- before(:each) do
172
- mock_env = double(:environment, desired_contents: env_contents)
173
- allow(subject).to receive(:environment).and_return(mock_env)
111
+ has_some_data = loaded_content.values.none?(&:empty?)
112
+ expect(has_some_data).to be true
174
113
  end
175
114
 
176
- it "includes environment's desired_contents" do
177
- expect(subject.purge_exclusions).to match_array(managed_dirs + env_contents)
115
+ it "raises if Puppetfile doesn't exist" do
116
+ path = '/rando/path/that/wont/exist'
117
+ subject = described_class.new(path, {})
118
+ expect {
119
+ subject.load!
120
+ }.to raise_error(/No such file or directory.*\/rando\/path\/.*/)
178
121
  end
179
122
  end
180
123
  end
181
124
 
182
- describe '#managed_directories' do
183
- it 'returns an array of paths that can be purged' do
184
- allow(R10K::Module).to receive(:new).with('puppet/test_module', subject.moduledir, hash_including(version: '1.2.3'), anything).and_call_original
125
+ describe 'default_branch_override' do
126
+ it 'is passed correctly to module loader init' do
127
+ # This path doesn't matter so long as it has a Puppetfile within it
128
+ path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
129
+ subject = described_class.new(path, {overrides: {environments: {default_branch_override: 'foo'}}})
185
130
 
186
- subject.add_module('puppet/test_module', '1.2.3')
187
- expect(subject.managed_directories).to match_array(["/some/nonexistent/basedir/modules"])
188
- end
131
+ repo = instance_double('R10K::Git::StatefulRepository')
132
+ allow(repo).to receive(:resolve).with('foo').and_return(true)
133
+ allow(R10K::Git::StatefulRepository).to receive(:new).and_return(repo)
189
134
 
190
- context 'with a module with install_path == \'\'' do
191
- it 'basedir isn\'t in the list of paths to purge' do
192
- module_opts = { install_path: '', git: 'git@example.com:puppet/test_module.git' }
135
+ allow(subject.loader).to receive(:puppetfile_content).and_return <<-EOPF
136
+ # Track control branch and fall-back to main if no matching branch.
137
+ mod 'hieradata',
138
+ :git => 'git@git.example.com:organization/hieradata.git',
139
+ :branch => :control_branch,
140
+ :default_branch => 'main'
141
+ EOPF
193
142
 
194
- allow(R10K::Module).to receive(:new).with('puppet/test_module', subject.basedir, module_opts, anything).and_call_original
143
+ expect(subject.logger).not_to receive(:warn).
144
+ with(/Mismatch between passed and initialized.*preferring passed value/)
195
145
 
196
- subject.add_module('puppet/test_module', module_opts)
197
- expect(subject.managed_directories).to be_empty
198
- end
199
- end
200
- end
146
+ subject.load
201
147
 
202
- describe "evaluating a Puppetfile" do
203
- def expect_wrapped_error(orig, pf_path, wrapped_error)
204
- expect(orig).to be_a_kind_of(R10K::Error)
205
- expect(orig.message).to eq("Failed to evaluate #{pf_path}")
206
- expect(orig.original).to be_a_kind_of(wrapped_error)
148
+ loaded_module = subject.modules.first
149
+ expect(loaded_module.version).to eq('foo')
207
150
  end
208
151
 
209
- it "wraps and re-raises syntax errors" do
210
- path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'invalid-syntax')
211
- pf_path = File.join(path, 'Puppetfile')
212
- subject = described_class.new(path, {})
213
- expect {
214
- subject.load!
215
- }.to raise_error do |e|
216
- expect_wrapped_error(e, pf_path, SyntaxError)
217
- end
218
- end
152
+ it 'overrides module loader init if needed' do
153
+ # This path doesn't matter so long as it has a Puppetfile within it
154
+ path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
155
+ subject = described_class.new(path, {overrides: {environments: {default_branch_override: 'foo'}}})
219
156
 
220
- it "wraps and re-raises load errors" do
221
- path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'load-error')
222
- pf_path = File.join(path, 'Puppetfile')
223
- subject = described_class.new(path, {})
224
- expect {
225
- subject.load!
226
- }.to raise_error do |e|
227
- expect_wrapped_error(e, pf_path, LoadError)
228
- end
229
- end
157
+ repo = instance_double('R10K::Git::StatefulRepository')
158
+ allow(repo).to receive(:resolve).with('bar').and_return(true)
159
+ allow(R10K::Git::StatefulRepository).to receive(:new).and_return(repo)
230
160
 
231
- it "wraps and re-raises argument errors" do
232
- path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'argument-error')
233
- pf_path = File.join(path, 'Puppetfile')
234
- subject = described_class.new(path, {})
235
- expect {
236
- subject.load!
237
- }.to raise_error do |e|
238
- expect_wrapped_error(e, pf_path, ArgumentError)
239
- end
240
- end
161
+ allow(subject.loader).to receive(:puppetfile_content).and_return <<-EOPF
162
+ # Track control branch and fall-back to main if no matching branch.
163
+ mod 'hieradata',
164
+ :git => 'git@git.example.com:organization/hieradata.git',
165
+ :branch => :control_branch,
166
+ :default_branch => 'main'
167
+ EOPF
241
168
 
242
- it "rejects Puppetfiles with duplicate module names" do
243
- path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'duplicate-module-error')
244
- pf_path = File.join(path, 'Puppetfile')
245
- subject = described_class.new(path, {})
246
- expect {
247
- subject.load!
248
- }.to raise_error(R10K::Error, /Puppetfiles cannot contain duplicate module names/i)
249
- end
169
+ expect(subject.logger).to receive(:warn).
170
+ with(/Mismatch between passed and initialized.*preferring passed value/)
250
171
 
251
- it "wraps and re-raises name errors" do
252
- path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'name-error')
253
- pf_path = File.join(path, 'Puppetfile')
254
- subject = described_class.new(path, {})
255
- expect {
256
- subject.load!
257
- }.to raise_error do |e|
258
- expect_wrapped_error(e, pf_path, NameError)
259
- end
172
+ subject.load('bar')
173
+ loaded_module = subject.modules.first
174
+ expect(loaded_module.version).to eq('bar')
260
175
  end
261
176
 
262
- it "accepts a forge module with a version" do
177
+ it 'does not warn if passed and initialized default_branch_overrides match' do
178
+ # This path doesn't matter so long as it has a Puppetfile within it
263
179
  path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-with-version')
264
- pf_path = File.join(path, 'Puppetfile')
265
- subject = described_class.new(path, {})
266
- expect { subject.load! }.not_to raise_error
267
- end
268
-
269
- it "accepts a forge module without a version" do
270
- path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'valid-forge-without-version')
271
- pf_path = File.join(path, 'Puppetfile')
272
- subject = described_class.new(path, {})
273
- expect { subject.load! }.not_to raise_error
274
- end
275
-
276
- it "creates a git module and applies the default branch sepcified in the Puppetfile" do
277
- path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'default-branch-override')
278
- pf_path = File.join(path, 'Puppetfile')
279
- subject = described_class.new(path, {})
280
- expect { subject.load! }.not_to raise_error
281
- git_module = subject.modules[0]
282
- expect(git_module.default_ref).to eq 'here_lies_the_default_branch'
283
- end
284
-
285
- it "creates a git module and applies the provided default_branch_override" do
286
- path = File.join(PROJECT_ROOT, 'spec', 'fixtures', 'unit', 'puppetfile', 'default-branch-override')
287
- pf_path = File.join(path, 'Puppetfile')
288
- subject = described_class.new(path, {})
289
- default_branch_override = 'default_branch_override_name'
290
- expect { subject.load!(default_branch_override) }.not_to raise_error
291
- git_module = subject.modules[0]
292
- expect(git_module.default_override_ref).to eq default_branch_override
293
- expect(git_module.default_ref).to eq "here_lies_the_default_branch"
180
+ subject = described_class.new(path, {overrides: {environments: {default_branch_override: 'foo'}}})
181
+
182
+ repo = instance_double('R10K::Git::StatefulRepository')
183
+ allow(repo).to receive(:resolve).with('foo').and_return(true)
184
+ allow(R10K::Git::StatefulRepository).to receive(:new).and_return(repo)
185
+
186
+ allow(subject.loader).to receive(:puppetfile_content).and_return <<-EOPF
187
+ # Track control branch and fall-back to main if no matching branch.
188
+ mod 'hieradata',
189
+ :git => 'git@git.example.com:organization/hieradata.git',
190
+ :branch => :control_branch,
191
+ :default_branch => 'main'
192
+ EOPF
193
+
194
+ expect(subject.logger).not_to receive(:warn).
195
+ with(/Mismatch between passed and initialized.*preferring passed value/)
196
+
197
+ subject.load('foo')
198
+ loaded_module = subject.modules.first
199
+ expect(loaded_module.version).to eq('foo')
294
200
  end
295
201
  end
296
202