r10k 3.5.1 → 3.9.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +4 -1
  3. data/.github/workflows/docker.yml +4 -1
  4. data/.github/workflows/release.yml +3 -2
  5. data/.github/workflows/rspec_tests.yml +81 -0
  6. data/.travis.yml +8 -1
  7. data/CHANGELOG.mkd +43 -2
  8. data/CODEOWNERS +2 -2
  9. data/README.mkd +13 -4
  10. data/doc/common-patterns.mkd +1 -0
  11. data/doc/dynamic-environments/configuration.mkd +143 -39
  12. data/doc/dynamic-environments/usage.mkd +12 -11
  13. data/doc/puppetfile.mkd +23 -3
  14. data/docker/Gemfile +1 -1
  15. data/docker/Makefile +7 -4
  16. data/docker/docker-compose.yml +18 -0
  17. data/docker/r10k/Dockerfile +4 -3
  18. data/docker/r10k/docker-entrypoint.sh +0 -1
  19. data/docker/r10k/release.Dockerfile +3 -2
  20. data/docker/spec/dockerfile_spec.rb +26 -32
  21. data/integration/tests/git_source/git_source_repeated_remote.rb +68 -0
  22. data/integration/tests/user_scenario/basic_workflow/multi_env_custom_forge_git_module.rb +2 -1
  23. data/integration/tests/user_scenario/basic_workflow/multi_env_custom_forge_git_module_static.rb +2 -1
  24. data/integration/tests/user_scenario/basic_workflow/multi_source_custom_forge_git_module.rb +1 -1
  25. data/integration/tests/user_scenario/basic_workflow/single_env_custom_forge_git_module.rb +2 -1
  26. data/integration/tests/user_scenario/complex_workflow/multi_env_add_change_remove.rb +1 -1
  27. data/integration/tests/user_scenario/complex_workflow/multi_env_remove_re-add.rb +1 -1
  28. data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +1 -1
  29. data/lib/r10k/action/deploy/display.rb +9 -3
  30. data/lib/r10k/action/deploy/environment.rb +39 -14
  31. data/lib/r10k/action/deploy/module.rb +4 -1
  32. data/lib/r10k/action/runner.rb +34 -0
  33. data/lib/r10k/cli/deploy.rb +14 -7
  34. data/lib/r10k/cli/puppetfile.rb +5 -5
  35. data/lib/r10k/environment/base.rb +9 -2
  36. data/lib/r10k/environment/git.rb +17 -2
  37. data/lib/r10k/environment/name.rb +22 -4
  38. data/lib/r10k/environment/svn.rb +11 -2
  39. data/lib/r10k/environment/with_modules.rb +28 -20
  40. data/lib/r10k/forge/module_release.rb +2 -2
  41. data/lib/r10k/git.rb +1 -0
  42. data/lib/r10k/git/cache.rb +12 -4
  43. data/lib/r10k/git/rugged/credentials.rb +39 -2
  44. data/lib/r10k/git/stateful_repository.rb +4 -0
  45. data/lib/r10k/initializers.rb +2 -0
  46. data/lib/r10k/module/base.rb +8 -0
  47. data/lib/r10k/module/forge.rb +16 -4
  48. data/lib/r10k/module/git.rb +42 -24
  49. data/lib/r10k/module/local.rb +1 -1
  50. data/lib/r10k/module/svn.rb +14 -11
  51. data/lib/r10k/puppetfile.rb +30 -12
  52. data/lib/r10k/settings.rb +30 -3
  53. data/lib/r10k/source/base.rb +5 -0
  54. data/lib/r10k/source/git.rb +26 -3
  55. data/lib/r10k/source/hash.rb +4 -2
  56. data/lib/r10k/source/svn.rb +5 -1
  57. data/lib/r10k/util/setopts.rb +33 -12
  58. data/lib/r10k/version.rb +1 -1
  59. data/locales/r10k.pot +71 -43
  60. data/r10k.gemspec +1 -1
  61. data/spec/fixtures/unit/action/r10k_creds.yaml +9 -0
  62. data/spec/shared-examples/subprocess-runner.rb +11 -5
  63. data/spec/unit/action/deploy/display_spec.rb +4 -0
  64. data/spec/unit/action/deploy/environment_spec.rb +154 -12
  65. data/spec/unit/action/deploy/module_spec.rb +40 -1
  66. data/spec/unit/action/puppetfile/install_spec.rb +1 -0
  67. data/spec/unit/action/runner_spec.rb +48 -1
  68. data/spec/unit/environment/git_spec.rb +19 -2
  69. data/spec/unit/environment/name_spec.rb +28 -0
  70. data/spec/unit/environment/svn_spec.rb +12 -0
  71. data/spec/unit/environment/with_modules_spec.rb +74 -0
  72. data/spec/unit/forge/module_release_spec.rb +14 -10
  73. data/spec/unit/git/cache_spec.rb +10 -0
  74. data/spec/unit/git/rugged/credentials_spec.rb +79 -2
  75. data/spec/unit/git_spec.rb +3 -3
  76. data/spec/unit/module/forge_spec.rb +6 -0
  77. data/spec/unit/module/git_spec.rb +56 -1
  78. data/spec/unit/module_spec.rb +59 -9
  79. data/spec/unit/puppetfile_spec.rb +61 -7
  80. data/spec/unit/settings_spec.rb +12 -0
  81. data/spec/unit/source/git_spec.rb +49 -1
  82. data/spec/unit/util/setopts_spec.rb +25 -1
  83. metadata +9 -11
  84. data/azure-pipelines.yml +0 -86
data/r10k.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.license = 'Apache-2.0'
24
24
 
25
25
  s.add_dependency 'colored2', '3.1.2'
26
- s.add_dependency 'cri', ['>= 2.15.10', '< 3.0.0']
26
+ s.add_dependency 'cri', '2.15.10'
27
27
 
28
28
  s.add_dependency 'log4r', '1.1.10'
29
29
  s.add_dependency 'multi_json', '~> 1.10'
@@ -0,0 +1,9 @@
1
+ ---
2
+ git:
3
+ private_key: '/global/config/private/key'
4
+ oauth_token: '/global/config/oauth/token'
5
+ repositories:
6
+ - remote: 'git@myfakegitserver.com:user/repo.git'
7
+ private_key: '/config/private/key'
8
+ - remote: 'https://myfakegitserver.com/user/repo.git'
9
+ oauth_token: '/config/oauth/token'
@@ -32,23 +32,29 @@ shared_examples_for "a subprocess runner" do |fixture_root|
32
32
  end
33
33
  end
34
34
 
35
- describe "running 'ls' with a different working directory" do
35
+ describe "running 'ls' or 'dir' with a different working directory" do
36
36
  subject do
37
- described_class.new(%w[ls]).tap do |o|
38
- o.cwd = fixture_root
37
+ if R10K::Util::Platform.windows?
38
+ described_class.new(%w[cmd /c dir]).tap do |o|
39
+ o.cwd = fixture_root
40
+ end
41
+ else
42
+ described_class.new(%w[ls]).tap do |o|
43
+ o.cwd = fixture_root
44
+ end
39
45
  end
40
46
  end
41
47
 
42
48
  it "returns the contents of the given working directory" do
43
49
  result = subject.run
44
- expect(result.stdout).to eq 'no-execute.sh'
50
+ expect(result.stdout).to match('no-execute.sh')
45
51
  end
46
52
  end
47
53
 
48
54
  describe "running 'false'" do
49
55
  subject { described_class.new(%w[false]) }
50
56
 
51
- it "sets the exit code to 1" do
57
+ it "sets the exit code to 1", unless: R10K::Util::Platform.windows? do
52
58
  result = subject.run
53
59
  expect(result.exit_code).to eq 1
54
60
  end
@@ -8,6 +8,10 @@ describe R10K::Action::Deploy::Display do
8
8
  described_class.new({puppetfile: true}, [])
9
9
  end
10
10
 
11
+ it "accepts a modules option" do
12
+ described_class.new({modules: true}, [])
13
+ end
14
+
11
15
  it "accepts a detail option" do
12
16
  described_class.new({detail: true}, [])
13
17
  end
@@ -19,6 +19,10 @@ describe R10K::Action::Deploy::Environment do
19
19
  described_class.new({puppetfile: true}, [])
20
20
  end
21
21
 
22
+ it "can accept a modules option" do
23
+ described_class.new({modules: true}, [])
24
+ end
25
+
22
26
  it "can accept a default_branch_override option" do
23
27
  described_class.new({:'default-branch-override' => 'default_branch_override_name'}, [])
24
28
  end
@@ -27,8 +31,6 @@ describe R10K::Action::Deploy::Environment do
27
31
  described_class.new({:'no-force' => true}, [])
28
32
  end
29
33
 
30
- it "normalizes environment names in the arg vector"
31
-
32
34
  it 'can accept a generate-types option' do
33
35
  described_class.new({ 'generate-types': true }, [])
34
36
  end
@@ -36,6 +38,25 @@ describe R10K::Action::Deploy::Environment do
36
38
  it 'can accept a puppet-path option' do
37
39
  described_class.new({ 'puppet-path': '/nonexistent' }, [])
38
40
  end
41
+
42
+ it 'can accept a private-key option' do
43
+ described_class.new({ 'private-key': '/nonexistent' }, [])
44
+ end
45
+
46
+ it 'can accept a token option' do
47
+ described_class.new({ 'oauth-token': '/nonexistent' }, [])
48
+ end
49
+
50
+ describe "initializing errors" do
51
+ let (:settings) { { deploy: { purge_levels: [:environment],
52
+ purge_whitelist: ['coolfile', 'coolfile2'],
53
+ purge_allowlist: ['anothercoolfile']}}}
54
+
55
+ subject { described_class.new({config: "/some/nonexistent/path"}, [], settings)}
56
+ it 'errors out when both purge_whitelist and purge_allowlist are set' do
57
+ expect{subject}.to raise_error(R10K::Error, /Values found for both purge_whitelist and purge_allowlist./)
58
+ end
59
+ end
39
60
  end
40
61
 
41
62
  describe "when called" do
@@ -52,6 +73,29 @@ describe R10K::Action::Deploy::Environment do
52
73
  )
53
74
  end
54
75
 
76
+ describe "with puppetfile or modules flag" do
77
+ let(:deployment) { R10K::Deployment.new(mock_config) }
78
+ let(:puppetfile) { instance_double("R10K::Puppetfile", modules: []).as_null_object }
79
+
80
+ before do
81
+ expect(R10K::Deployment).to receive(:new).and_return(deployment)
82
+ expect(R10K::Puppetfile).to receive(:new).and_return(puppetfile).at_least(:once)
83
+ end
84
+
85
+ it "syncs the puppetfile when given the puppetfile flag" do
86
+ expect(puppetfile).to receive(:accept).and_return([])
87
+ action = described_class.new({config: "/some/nonexistent/path", puppetfile: true}, [])
88
+ action.call
89
+ end
90
+
91
+ it "syncs the puppetfile when given the modules flag" do
92
+ expect(puppetfile).to receive(:accept).and_return([])
93
+ action = described_class.new({config: "/some/nonexistent/path", modules: true}, [])
94
+ action.call
95
+ end
96
+
97
+ end
98
+
55
99
  describe "with an environment that doesn't exist" do
56
100
  let(:deployment) do
57
101
  R10K::Deployment.new(mock_config)
@@ -71,7 +115,7 @@ describe R10K::Action::Deploy::Environment do
71
115
  end
72
116
 
73
117
  describe "with no-force" do
74
- subject { described_class.new({ config: "/some/nonexistent/path", puppetfile: true, :'no-force' => true}, %w[first]) }
118
+ subject { described_class.new({ config: "/some/nonexistent/path", modules: true, :'no-force' => true}, %w[first]) }
75
119
 
76
120
  it "tries to preserve local modifications" do
77
121
  expect(subject.force).to equal(false)
@@ -162,6 +206,44 @@ describe R10K::Action::Deploy::Environment do
162
206
  end
163
207
  end
164
208
 
209
+ describe 'extracting credentials' do
210
+ let(:deployment) do
211
+ R10K::Deployment.new(mock_config)
212
+ end
213
+
214
+ end
215
+
216
+ describe "Purging white/allowlist" do
217
+
218
+ let(:settings) { { deploy: { purge_levels: [:environment], purge_allowlist: ['coolfile', 'coolfile2'] } } }
219
+
220
+ let(:deployment) do
221
+ R10K::Deployment.new(mock_config.merge(settings))
222
+ end
223
+
224
+ before do
225
+ expect(R10K::Deployment).to receive(:new).and_return(deployment)
226
+ end
227
+
228
+ subject { described_class.new({ config: "/some/nonexistent/path", modules: true }, %w[PREFIX_first], settings) }
229
+
230
+ it "reads in the purge_allowlist setting and purges accordingly" do
231
+ expect(subject.logger).to receive(:debug).with(/purging unmanaged content for environment/i)
232
+ expect(subject.instance_variable_get(:@user_purge_allowlist)).to eq(['coolfile', 'coolfile2'])
233
+ subject.call
234
+ end
235
+
236
+ describe "purge_whitelist" do
237
+ let (:settings) { { deploy: { purge_levels: [:environment], purge_whitelist: ['coolfile', 'coolfile2'] } } }
238
+
239
+ it "reads in the purge_whitelist setting and still sets it to purge_allowlist and purges accordingly" do
240
+ expect(subject.logger).to receive(:debug).with(/purging unmanaged content for environment/i)
241
+ expect(subject.instance_variable_get(:@user_purge_allowlist)).to eq(['coolfile', 'coolfile2'])
242
+ subject.call
243
+ end
244
+ end
245
+ end
246
+
165
247
  describe "purge_levels" do
166
248
  let(:settings) { { deploy: { purge_levels: purge_levels } } }
167
249
 
@@ -173,7 +255,7 @@ describe R10K::Action::Deploy::Environment do
173
255
  expect(R10K::Deployment).to receive(:new).and_return(deployment)
174
256
  end
175
257
 
176
- subject { described_class.new({ config: "/some/nonexistent/path", puppetfile: true }, %w[PREFIX_first], settings) }
258
+ subject { described_class.new({ config: "/some/nonexistent/path", modules: true }, %w[PREFIX_first], settings) }
177
259
 
178
260
  describe "deployment purge level" do
179
261
  let(:purge_levels) { [:deployment] }
@@ -219,6 +301,7 @@ describe R10K::Action::Deploy::Environment do
219
301
  end
220
302
  end
221
303
  end
304
+
222
305
  describe "generate-types" do
223
306
  let(:deployment) do
224
307
  R10K::Deployment.new(
@@ -248,7 +331,7 @@ describe R10K::Action::Deploy::Environment do
248
331
  described_class.new(
249
332
  {
250
333
  config: '/some/nonexistent/path',
251
- puppetfile: true,
334
+ modules: true,
252
335
  'generate-types': true
253
336
  },
254
337
  %w[first second]
@@ -302,7 +385,7 @@ describe R10K::Action::Deploy::Environment do
302
385
  described_class.new(
303
386
  {
304
387
  config: '/some/nonexistent/path',
305
- puppetfile: true,
388
+ modules: true,
306
389
  'generate-types': false
307
390
  },
308
391
  %w[first]
@@ -331,6 +414,33 @@ describe R10K::Action::Deploy::Environment do
331
414
  expect(subject.instance_variable_get(:@puppet_path)).to eq('/nonexistent')
332
415
  end
333
416
  end
417
+
418
+ describe 'with puppet-conf' do
419
+
420
+ subject { described_class.new({ config: '/some/nonexistent/path', 'puppet-conf': '/nonexistent' }, []) }
421
+
422
+ it 'sets puppet_conf' do
423
+ expect(subject.instance_variable_get(:@puppet_conf)).to eq('/nonexistent')
424
+ end
425
+ end
426
+
427
+ describe 'with private-key' do
428
+
429
+ subject { described_class.new({ config: '/some/nonexistent/path', 'private-key': '/nonexistent' }, []) }
430
+
431
+ it 'sets private_key' do
432
+ expect(subject.instance_variable_get(:@private_key)).to eq('/nonexistent')
433
+ end
434
+ end
435
+
436
+ describe 'with oauth-token' do
437
+
438
+ subject { described_class.new({ config: '/some/nonexistent/path', 'oauth-token': '/nonexistent' }, []) }
439
+
440
+ it 'sets oauth_token' do
441
+ expect(subject.instance_variable_get(:@oauth_token)).to eq('/nonexistent')
442
+ end
443
+ end
334
444
  end
335
445
 
336
446
  describe "write_environment_info!" do
@@ -349,9 +459,25 @@ describe R10K::Action::Deploy::Environment do
349
459
 
350
460
  let(:mock_stateful_repo_1) { instance_double("R10K::Git::StatefulRepository", :head => "123456") }
351
461
  let(:mock_stateful_repo_2) { instance_double("R10K::Git::StatefulRepository", :head => "654321") }
352
- let(:mock_git_module_1) { instance_double("R10K::Module::Git", :name => "my_cool_module", :version => "1.0", :repo => mock_stateful_repo_1) }
353
- let(:mock_git_module_2) { instance_double("R10K::Module::Git", :name => "my_lame_module", :version => "0.0.1", :repo => mock_stateful_repo_2) }
354
- let(:mock_forge_module_1) { double(:name => "their_shiny_module", :version => "2.0.0") }
462
+ let(:mock_git_module_1) do
463
+ instance_double("R10K::Module::Git",
464
+ :name => "my_cool_module",
465
+ :properties => {
466
+ :type => :git,
467
+ :expected => "1.0",
468
+ :actual => mock_stateful_repo_1.head
469
+ })
470
+ end
471
+ let(:mock_git_module_2) do
472
+ instance_double("R10K::Module::Git",
473
+ :name => "my_uncool_module",
474
+ :properties => {
475
+ :type => :git,
476
+ :expected => "0.0.1",
477
+ :actual => mock_stateful_repo_2.head
478
+ })
479
+ end
480
+ let(:mock_forge_module_1) { double(:name => "their_shiny_module", :properties => { :expected => "2.0.0" }) }
355
481
  let(:mock_puppetfile) { instance_double("R10K::Puppetfile", :modules => [mock_git_module_1, mock_git_module_2, mock_forge_module_1]) }
356
482
 
357
483
  before(:all) do
@@ -364,9 +490,8 @@ describe R10K::Action::Deploy::Environment do
364
490
  Dir.delete(@tmp_path)
365
491
  end
366
492
 
367
- it "writes the .r10k-deploy file correctly" do
493
+ it "writes the .r10k-deploy file correctly if all goes well" do
368
494
  allow(R10K::Puppetfile).to receive(:new).and_return(mock_puppetfile)
369
- allow(mock_forge_module_1).to receive(:repo).and_raise(NoMethodError)
370
495
 
371
496
  fake_env = Fake_Environment.new(@tmp_path, {:name => "my_cool_environment", :signature => "pablo picasso"})
372
497
  allow(fake_env).to receive(:modules).and_return(mock_puppetfile.modules)
@@ -383,13 +508,30 @@ describe R10K::Action::Deploy::Environment do
383
508
  expect(r10k_deploy['module_deploys'][0]['name']).to eq("my_cool_module")
384
509
  expect(r10k_deploy['module_deploys'][0]['version']).to eq("1.0")
385
510
  expect(r10k_deploy['module_deploys'][0]['sha']).to eq("123456")
386
- expect(r10k_deploy['module_deploys'][1]['name']).to eq("my_lame_module")
511
+ expect(r10k_deploy['module_deploys'][1]['name']).to eq("my_uncool_module")
387
512
  expect(r10k_deploy['module_deploys'][1]['version']).to eq("0.0.1")
388
513
  expect(r10k_deploy['module_deploys'][1]['sha']).to eq("654321")
389
514
  expect(r10k_deploy['module_deploys'][2]['name']).to eq("their_shiny_module")
390
515
  expect(r10k_deploy['module_deploys'][2]['version']).to eq("2.0.0")
391
516
  expect(r10k_deploy['module_deploys'][2]['sha']).to eq(nil)
517
+ end
518
+
519
+ it "writes the .r10k-deploy file correctly if there's a failure" do
520
+ allow(R10K::Puppetfile).to receive(:new).and_return(mock_puppetfile)
392
521
 
522
+ fake_env = Fake_Environment.new(@tmp_path, {:name => "my_cool_environment", :signature => "pablo picasso"})
523
+ allow(fake_env).to receive(:modules).and_return(mock_puppetfile.modules)
524
+ allow(mock_forge_module_1).to receive(:properties).and_raise(StandardError)
525
+ subject.send(:write_environment_info!, fake_env, "2019-01-01 23:23:22 +0000", true)
526
+
527
+ file_contents = File.read("#{@tmp_path}/.r10k-deploy.json")
528
+ r10k_deploy = JSON.parse(file_contents)
529
+
530
+ expect(r10k_deploy['name']).to eq("my_cool_environment")
531
+ expect(r10k_deploy['signature']).to eq("pablo picasso")
532
+ expect(r10k_deploy['started_at']).to eq("2019-01-01 23:23:22 +0000")
533
+ expect(r10k_deploy['deploy_success']).to eq(true)
534
+ expect(r10k_deploy['module_deploys'].length).to eq(0)
393
535
  end
394
536
  end
395
537
  end
@@ -26,9 +26,21 @@ describe R10K::Action::Deploy::Module do
26
26
  described_class.new({ 'puppet-path': '/nonexistent' }, [])
27
27
  end
28
28
 
29
+ it 'can accept a puppet-conf option' do
30
+ described_class.new({ 'puppet-conf': '/nonexistent' }, [])
31
+ end
32
+
29
33
  it 'can accept a cachedir option' do
30
34
  described_class.new({ cachedir: '/nonexistent' }, [])
31
35
  end
36
+
37
+ it 'can accept a private-key option' do
38
+ described_class.new({ 'private-key': '/nonexistent' }, [])
39
+ end
40
+
41
+ it 'can accept a token option' do
42
+ described_class.new({ 'oauth-token': '/nonexistent' }, [])
43
+ end
32
44
  end
33
45
 
34
46
  describe "with no-force" do
@@ -128,12 +140,39 @@ describe R10K::Action::Deploy::Module do
128
140
  end
129
141
  end
130
142
 
143
+ describe 'with puppet-conf' do
144
+
145
+ subject { described_class.new({ config: '/some/nonexistent/path', 'puppet-conf': '/nonexistent' }, []) }
146
+
147
+ it 'sets puppet_conf' do
148
+ expect(subject.instance_variable_get(:@puppet_conf)).to eq('/nonexistent')
149
+ end
150
+ end
151
+
131
152
  describe 'with cachedir' do
132
153
 
133
154
  subject { described_class.new({ config: '/some/nonexistent/path', cachedir: '/nonexistent' }, []) }
134
155
 
135
- it 'sets puppet_path' do
156
+ it 'sets cachedir' do
136
157
  expect(subject.instance_variable_get(:@cachedir)).to eq('/nonexistent')
137
158
  end
138
159
  end
160
+
161
+ describe 'with private-key' do
162
+
163
+ subject { described_class.new({ config: '/some/nonexistent/path', 'private-key': '/nonexistent' }, []) }
164
+
165
+ it 'sets private_key' do
166
+ expect(subject.instance_variable_get(:@private_key)).to eq('/nonexistent')
167
+ end
168
+ end
169
+
170
+ describe 'with oauth-token' do
171
+
172
+ subject { described_class.new({ config: '/some/nonexistent/path', 'oauth-token': '/nonexistent' }, []) }
173
+
174
+ it 'sets token_path' do
175
+ expect(subject.instance_variable_get(:@oauth_token)).to eq('/nonexistent')
176
+ end
177
+ end
139
178
  end
@@ -27,6 +27,7 @@ describe R10K::Action::Puppetfile::Install do
27
27
  before do
28
28
  allow(puppetfile).to receive(:purge!)
29
29
  allow(puppetfile).to receive(:modules).and_return(modules)
30
+ allow(puppetfile).to receive(:modules_by_vcs_cachedir).and_return({none: modules})
30
31
  end
31
32
 
32
33
  it "syncs each module in the Puppetfile" do
@@ -10,11 +10,12 @@ describe R10K::Action::Runner do
10
10
  Class.new do
11
11
  attr_reader :opts
12
12
  attr_reader :argv
13
+ attr_reader :settings
13
14
 
14
15
  def initialize(opts, argv, settings = {})
15
16
  @opts = opts
16
17
  @argv = argv
17
- @settings = {}
18
+ @settings = settings
18
19
  end
19
20
 
20
21
  def call
@@ -170,6 +171,52 @@ describe R10K::Action::Runner do
170
171
  end
171
172
  end
172
173
 
174
+ describe "configuring git credentials" do
175
+ it 'errors if both token and key paths are passed' do
176
+ runner = described_class.new({ 'oauth-token': '/nonexistent',
177
+ 'private-key': '/also/fake' }, %w[args yes], action_class)
178
+ expect{ runner.call }.to raise_error(R10K::Error, /Cannot specify both/)
179
+ end
180
+
181
+ it 'saves the sshkey path in settings hash' do
182
+ runner = described_class.new({ 'private-key': '/my/ssh/key' }, %w[args yes], action_class)
183
+ runner.call
184
+ expect(runner.instance.settings[:git][:private_key]).to eq('/my/ssh/key')
185
+ end
186
+
187
+ it 'overrides per-repo sshkey in settings hash' do
188
+ runner = described_class.new({ config: "spec/fixtures/unit/action/r10k_creds.yaml",
189
+ 'private-key': '/my/ssh/key' },
190
+ %w[args yes],
191
+ action_class)
192
+ runner.call
193
+ expect(runner.instance.settings[:git][:private_key]).to eq('/my/ssh/key')
194
+ expect(runner.instance.settings[:git][:repositories].count).to eq(2)
195
+ runner.instance.settings[:git][:repositories].each do |repo_settings|
196
+ expect(repo_settings[:private_key]).to eq('/my/ssh/key')
197
+ end
198
+ end
199
+
200
+ it 'saves the token path in settings hash' do
201
+ runner = described_class.new({ 'oauth-token': '/my/token/path' }, %w[args yes], action_class)
202
+ runner.call
203
+ expect(runner.instance.settings[:git][:oauth_token]).to eq('/my/token/path')
204
+ end
205
+
206
+ it 'overrides per-repo oauth token in settings hash' do
207
+ runner = described_class.new({ config: "spec/fixtures/unit/action/r10k_creds.yaml",
208
+ 'oauth-token': '/my/token' },
209
+ %w[args yes],
210
+ action_class)
211
+ runner.call
212
+ expect(runner.instance.settings[:git][:oauth_token]).to eq('/my/token')
213
+ expect(runner.instance.settings[:git][:repositories].count).to eq(2)
214
+ runner.instance.settings[:git][:repositories].each do |repo_settings|
215
+ expect(repo_settings[:oauth_token]).to eq('/my/token')
216
+ end
217
+ end
218
+ end
219
+
173
220
  describe "configuration authorization" do
174
221
  context "when license is not present" do
175
222
  before(:each) do