r10k 2.2.2 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.mkd +25 -0
  3. data/Gemfile +1 -1
  4. data/doc/dynamic-environments/configuration.mkd +36 -11
  5. data/integration/tests/basic_functionality/proxy_with_pe_only_module.rb +128 -0
  6. data/lib/r10k/errors.rb +18 -0
  7. data/lib/r10k/forge/module_release.rb +3 -3
  8. data/lib/r10k/git.rb +53 -0
  9. data/lib/r10k/git/rugged/bare_repository.rb +16 -7
  10. data/lib/r10k/git/rugged/base_repository.rb +16 -2
  11. data/lib/r10k/git/rugged/credentials.rb +9 -0
  12. data/lib/r10k/git/rugged/working_repository.rb +18 -5
  13. data/lib/r10k/git/shellgit/bare_repository.rb +12 -3
  14. data/lib/r10k/git/shellgit/base_repository.rb +19 -3
  15. data/lib/r10k/git/shellgit/working_repository.rb +13 -3
  16. data/lib/r10k/initializers.rb +1 -0
  17. data/lib/r10k/settings.rb +41 -22
  18. data/lib/r10k/settings/collection.rb +25 -21
  19. data/lib/r10k/settings/definition.rb +14 -2
  20. data/lib/r10k/settings/helpers.rb +38 -0
  21. data/lib/r10k/settings/list.rb +107 -0
  22. data/lib/r10k/util/symbolize_keys.rb +6 -2
  23. data/lib/r10k/version.rb +1 -1
  24. data/r10k.gemspec +1 -1
  25. data/r10k.yaml.example +38 -12
  26. data/spec/shared-examples/git/bare_repository.rb +62 -0
  27. data/spec/shared-examples/git/working_repository.rb +57 -11
  28. data/spec/shared-examples/settings/ancestry.rb +44 -0
  29. data/spec/unit/forge/module_release_spec.rb +1 -1
  30. data/spec/unit/git/rugged/credentials_spec.rb +6 -0
  31. data/spec/unit/settings/collection_spec.rb +2 -1
  32. data/spec/unit/settings/definition_spec.rb +6 -5
  33. data/spec/unit/settings/inheritance_spec.rb +38 -0
  34. data/spec/unit/settings/list_spec.rb +88 -0
  35. data/spec/unit/settings_spec.rb +52 -23
  36. data/spec/unit/util/attempt_spec.rb +1 -1
  37. data/spec/unit/util/symbolize_keys_spec.rb +25 -9
  38. metadata +11 -4
@@ -36,7 +36,12 @@ class R10K::Git::Rugged::WorkingRepository < R10K::Git::Rugged::BaseRepository
36
36
  # alternate object database.
37
37
  options = {:credentials => credentials}
38
38
  options.merge!(:alternates => [File.join(opts[:reference], 'objects')]) if opts[:reference]
39
- @_rugged_repo = ::Rugged::Repository.clone_at(remote, @path.to_s, options)
39
+
40
+ proxy = R10K::Git.get_proxy_for_remote(remote)
41
+
42
+ R10K::Git.with_proxy(proxy) do
43
+ @_rugged_repo = ::Rugged::Repository.clone_at(remote, @path.to_s, options)
44
+ end
40
45
 
41
46
  if opts[:reference]
42
47
  alternates << File.join(opts[:reference], 'objects')
@@ -70,11 +75,19 @@ class R10K::Git::Rugged::WorkingRepository < R10K::Git::Rugged::BaseRepository
70
75
  end
71
76
  end
72
77
 
73
- def fetch(remote = 'origin')
74
- logger.debug1 { "Fetching remote '#{remote}' at #{@path}" }
78
+ def fetch(remote_name = 'origin')
79
+ logger.debug1 { "Fetching remote '#{remote_name}' at #{@path}" }
75
80
  options = {:credentials => credentials}
76
- refspecs = ["+refs/heads/*:refs/remotes/#{remote}/*"]
77
- results = with_repo { |repo| repo.fetch(remote, refspecs, options) }
81
+ refspecs = ["+refs/heads/*:refs/remotes/#{remote_name}/*"]
82
+
83
+ remote = remotes[remote_name]
84
+ proxy = R10K::Git.get_proxy_for_remote(remote)
85
+ results = nil
86
+
87
+ R10K::Git.with_proxy(proxy) do
88
+ results = with_repo { |repo| repo.fetch(remote_name, refspecs, options) }
89
+ end
90
+
78
91
  report_transfer(results, remote)
79
92
  rescue Rugged::SshError, Rugged::NetworkError => e
80
93
  raise R10K::Git::GitError.new(e.message, :git_dir => git_dir, :backtrace => e.backtrace)
@@ -21,11 +21,20 @@ class R10K::Git::ShellGit::BareRepository < R10K::Git::ShellGit::BaseRepository
21
21
  end
22
22
 
23
23
  def clone(remote)
24
- git ['clone', '--mirror', remote, git_dir.to_s]
24
+ proxy = R10K::Git.get_proxy_for_remote(remote)
25
+
26
+ R10K::Git.with_proxy(proxy) do
27
+ git ['clone', '--mirror', remote, git_dir.to_s]
28
+ end
25
29
  end
26
30
 
27
- def fetch
28
- git ['fetch', '--prune'], :git_dir => git_dir.to_s
31
+ def fetch(remote_name='origin')
32
+ remote = remotes[remote_name]
33
+ proxy = R10K::Git.get_proxy_for_remote(remote)
34
+
35
+ R10K::Git.with_proxy(proxy) do
36
+ git ['fetch', remote_name, '--prune'], :git_dir => git_dir.to_s
37
+ end
29
38
  end
30
39
 
31
40
  def exist?
@@ -1,4 +1,5 @@
1
1
  require 'r10k/git/shellgit'
2
+ require 'r10k/util/subprocess'
2
3
  require 'r10k/logging'
3
4
 
4
5
  class R10K::Git::ShellGit::BaseRepository
@@ -47,6 +48,23 @@ class R10K::Git::ShellGit::BaseRepository
47
48
  end
48
49
  end
49
50
 
51
+ # @return [Hash] Collection of remotes for this repo, keys are the remote name and values are the remote URL.
52
+ def remotes
53
+ result = git ['config', '--local', '--get-regexp', '^remote\..*\.url$'], :git_dir => git_dir.to_s, :raise_on_fail => false
54
+
55
+ if result.success?
56
+ Hash[
57
+ result.stdout.split("\n").collect do |remote|
58
+ matches = /^remote\.(.*)\.url (.*)$/.match(remote)
59
+
60
+ [matches[1], matches[2]]
61
+ end
62
+ ]
63
+ else
64
+ {}
65
+ end
66
+ end
67
+
50
68
  include R10K::Logging
51
69
 
52
70
  private
@@ -95,8 +113,6 @@ class R10K::Git::ShellGit::BaseRepository
95
113
  subproc.raise_on_fail = raise_on_fail
96
114
  subproc.logger = self.logger
97
115
 
98
- result = subproc.execute
99
-
100
- result
116
+ subproc.execute
101
117
  end
102
118
  end
@@ -32,7 +32,12 @@ class R10K::Git::ShellGit::WorkingRepository < R10K::Git::ShellGit::BaseReposito
32
32
  if opts[:reference]
33
33
  argv += ['--reference', opts[:reference]]
34
34
  end
35
- git argv
35
+
36
+ proxy = R10K::Git.get_proxy_for_remote(remote)
37
+
38
+ R10K::Git.with_proxy(proxy) do
39
+ git argv
40
+ end
36
41
 
37
42
  if opts[:ref]
38
43
  checkout(opts[:ref])
@@ -46,8 +51,13 @@ class R10K::Git::ShellGit::WorkingRepository < R10K::Git::ShellGit::BaseReposito
46
51
  git ['checkout', ref], :path => @path.to_s
47
52
  end
48
53
 
49
- def fetch
50
- git ['fetch'], :path => @path.to_s
54
+ def fetch(remote_name='origin')
55
+ remote = remotes[remote_name]
56
+ proxy = R10K::Git.get_proxy_for_remote(remote)
57
+
58
+ R10K::Git.with_proxy(proxy) do
59
+ git ['fetch', remote_name, '--prune'], :path => @path.to_s
60
+ end
51
61
  end
52
62
 
53
63
  def exist?
@@ -42,6 +42,7 @@ module R10K
42
42
  with_setting(:provider) { |value| R10K::Git.provider = value }
43
43
  with_setting(:username) { |value| R10K::Git.settings[:username] = value }
44
44
  with_setting(:private_key) { |value| R10K::Git.settings[:private_key] = value }
45
+ with_setting(:proxy) { |value| R10K::Git.settings[:proxy] = value }
45
46
  with_setting(:repositories) { |value| R10K::Git.settings[:repositories] = value }
46
47
  end
47
48
  end
@@ -7,6 +7,7 @@ module R10K
7
7
 
8
8
  require 'r10k/settings/collection'
9
9
  require 'r10k/settings/definition'
10
+ require 'r10k/settings/list'
10
11
 
11
12
  def self.git_settings
12
13
  R10K::Settings::Collection.new(:git, [
@@ -29,20 +30,33 @@ module R10K
29
30
  Only used by the 'rugged' Git provider.",
30
31
  }),
31
32
 
32
- Definition.new(:repositories, {
33
- :desc => "Repository specific configuration.",
34
- :default => [],
35
- :normalize => lambda do |repositories|
36
- # The config file loading logic recursively converts hash keys that are strings to symbols,
37
- # It doesn't understand hashes inside arrays though so we have to do this manually.
38
- repositories.map do |repo|
39
- repo.inject({}) do |retval, (key, value)|
40
- retval[key.to_sym] = value
41
- retval
42
- end
43
- end
44
- end
45
- }),
33
+ URIDefinition.new(:proxy, {
34
+ :desc => "An optional proxy server to use when interacting with Git sources via HTTP(S).",
35
+ :default => :inherit,
36
+ }),
37
+
38
+ List.new(:repositories, lambda {
39
+ R10K::Settings::Collection.new(nil, [
40
+ Definition.new(:remote, {
41
+ :desc => "Remote source that repository-specific settings should apply to.",
42
+ }),
43
+
44
+ Definition.new(:private_key, {
45
+ :desc => "The path to the SSH private key for Git SSH remotes.
46
+ Only used by the 'rugged' Git provider.",
47
+ :default => :inherit,
48
+ }),
49
+
50
+ URIDefinition.new(:proxy, {
51
+ :desc => "An optional proxy server to use when interacting with Git sources via HTTP(S).",
52
+ :default => :inherit,
53
+ }),
54
+ ])
55
+ },
56
+ {
57
+ :desc => "Repository specific configuration.",
58
+ :default => [],
59
+ }),
46
60
  ])
47
61
  end
48
62
 
@@ -50,14 +64,7 @@ module R10K
50
64
  R10K::Settings::Collection.new(:forge, [
51
65
  URIDefinition.new(:proxy, {
52
66
  :desc => "An optional proxy server to use when downloading modules from the forge.",
53
- :default => lambda do
54
- [
55
- ENV['HTTPS_PROXY'],
56
- ENV['https_proxy'],
57
- ENV['HTTP_PROXY'],
58
- ENV['http_proxy']
59
- ].find { |value| value }
60
- end
67
+ :default => :inherit,
61
68
  }),
62
69
 
63
70
  URIDefinition.new(:baseurl, {
@@ -104,6 +111,18 @@ module R10K
104
111
  end
105
112
  }),
106
113
 
114
+ URIDefinition.new(:proxy, {
115
+ :desc => "Proxy to use for all r10k operations which occur over HTTP(S).",
116
+ :default => lambda {
117
+ [
118
+ ENV['HTTPS_PROXY'],
119
+ ENV['https_proxy'],
120
+ ENV['HTTP_PROXY'],
121
+ ENV['http_proxy']
122
+ ].find { |value| value }
123
+ },
124
+ }),
125
+
107
126
  R10K::Settings.forge_settings,
108
127
 
109
128
  R10K::Settings.git_settings,
@@ -1,3 +1,4 @@
1
+ require 'r10k/settings/helpers'
1
2
  require 'r10k/settings/definition'
2
3
  require 'r10k/util/setopts'
3
4
  require 'r10k/util/symbolize_keys'
@@ -9,6 +10,7 @@ module R10K
9
10
  # Define a group of settings, which can be single definitions or nested
10
11
  # collections.
11
12
  class Collection
13
+ include R10K::Settings::Helpers
12
14
 
13
15
  # @!attribute [r] name
14
16
  # @return [String] The name of this collection
@@ -18,7 +20,14 @@ module R10K
18
20
  # @param settings [Array] All settings in this collection
19
21
  def initialize(name, settings)
20
22
  @name = name
21
- @settings = Hash[settings.map { |s| [s.name, s] }]
23
+
24
+ @settings = {}
25
+
26
+ # iterate through settings and adopt them
27
+ settings.each do |s|
28
+ s.parent = self
29
+ @settings[s.name] = s
30
+ end
22
31
  end
23
32
 
24
33
  # Assign new values, perform validation checks, and return the final
@@ -39,8 +48,8 @@ module R10K
39
48
  def assign(newvalues)
40
49
  return if newvalues.nil?
41
50
 
42
- # TODO: this results in inconsistency for hashes inside arrays.
43
51
  R10K::Util::SymbolizeKeys.symbolize_keys!(newvalues)
52
+
44
53
  @settings.each_pair do |name, setting|
45
54
  if newvalues.key?(name)
46
55
  setting.assign(newvalues[name])
@@ -64,7 +73,13 @@ module R10K
64
73
  end
65
74
 
66
75
  if !errors.empty?
67
- raise ValidationError.new("Validation failed for #{@name} settings group", :errors => errors)
76
+ if @name
77
+ msg = "Validation failed for '#{@name}' settings group"
78
+ else
79
+ msg = "Validation failed for settings group"
80
+ end
81
+
82
+ raise ValidationError.new(msg, :errors => errors)
68
83
  end
69
84
  end
70
85
 
@@ -72,12 +87,19 @@ module R10K
72
87
  # @return [Hash]
73
88
  def resolve
74
89
  rv = {}
90
+
75
91
  @settings.each_pair do |name, setting|
76
92
  rv[name] = setting.resolve
77
93
  end
94
+
78
95
  rv.freeze
79
96
  end
80
97
 
98
+ # Access individual settings via a Hash-like interface.
99
+ def [](name)
100
+ @settings[name]
101
+ end
102
+
81
103
  class ValidationError < R10K::Error
82
104
 
83
105
  attr_reader :errors
@@ -95,24 +117,6 @@ module R10K
95
117
  end
96
118
  struct.join("\n")
97
119
  end
98
-
99
- private
100
-
101
- def structure_exception(name, exc)
102
- struct = []
103
- struct << "#{name}:"
104
- if exc.is_a? ValidationError
105
- struct << indent(exc.format)
106
- else
107
- struct << indent(exc.message)
108
- end
109
- struct.join("\n")
110
- end
111
-
112
- def indent(str, level = 4)
113
- prefix = ' ' * level
114
- str.gsub(/^/, prefix)
115
- end
116
120
  end
117
121
  end
118
122
  end
@@ -1,3 +1,4 @@
1
+ require 'r10k/settings/helpers'
1
2
  require 'r10k/util/setopts'
2
3
 
3
4
  module R10K
@@ -6,10 +7,10 @@ module R10K
6
7
  # Define a single setting and additional attributes like descriptions,
7
8
  # default values, and validation.
8
9
  class Definition
9
-
10
10
  require 'r10k/settings/uri_definition'
11
11
  require 'r10k/settings/enum_definition'
12
12
 
13
+ include R10K::Settings::Helpers
13
14
  include R10K::Util::Setopts
14
15
 
15
16
  # @!attribute [r] name
@@ -90,7 +91,18 @@ module R10K
90
91
  if !@value.nil?
91
92
  @value
92
93
  elsif @default
93
- @default.is_a?(Proc) ? @default.call : @default
94
+ if @default == :inherit
95
+ # walk all the way up to root, starting with grandparent
96
+ ancestor = parent
97
+
98
+ while ancestor = ancestor.parent
99
+ return ancestor[@name].resolve if ancestor.respond_to?(:[]) && ancestor[@name]
100
+ end
101
+ elsif @default.is_a?(Proc)
102
+ @default.call
103
+ else
104
+ @default
105
+ end
94
106
  end
95
107
  end
96
108
 
@@ -0,0 +1,38 @@
1
+ require 'r10k/errors'
2
+ require 'r10k/settings/collection'
3
+
4
+ module R10K
5
+ module Settings
6
+ module Helpers
7
+ def self.included(klass)
8
+ klass.send(:include, InstanceMethods)
9
+ klass.send(:extend, ClassMethods)
10
+ end
11
+
12
+ module InstanceMethods
13
+ # Assign a parent collection to this setting. Parent may only be
14
+ # assigned once.
15
+ #
16
+ # @param new_parent [R10K::Settings::Collection] Parent collection
17
+ def parent=(new_parent)
18
+ unless @parent.nil?
19
+ raise R10K::Error.new("#{self.class} instances cannot be reassigned to a new parent.")
20
+ end
21
+
22
+ unless new_parent.is_a?(R10K::Settings::Collection) || new_parent.is_a?(R10K::Settings::List)
23
+ raise R10K::Error.new("#{self.class} instances may only belong to a settings collection or list.")
24
+ end
25
+
26
+ @parent = new_parent
27
+ end
28
+
29
+ def parent
30
+ @parent
31
+ end
32
+ end
33
+
34
+ module ClassMethods
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,107 @@
1
+ require 'r10k/settings/helpers'
2
+ require 'r10k/settings/collection'
3
+ require 'r10k/errors'
4
+ require 'r10k/util/setopts'
5
+
6
+ module R10K
7
+ module Settings
8
+
9
+ # A container for an arbitrarily long list of other settings.
10
+ class List
11
+ include R10K::Settings::Helpers
12
+ include R10K::Util::Setopts
13
+
14
+ # @!attribute [r] name
15
+ # @return [String] The name of this collection
16
+ attr_reader :name
17
+
18
+ # @param name [Symbol] The name of the setting for this definition.
19
+ # @param item_proc [#call] An object whose #call method will return a
20
+ # new instance of another R10K::Settings class to hold each item
21
+ # added to this list.
22
+ # @param opts [Hash] Additional options for this definition to control
23
+ # validation, normalization, and the like.
24
+ #
25
+ # @options opts [String] :desc Extended description of this setting.
26
+ # @options opts [Array] :default Initial/default contents of the list.
27
+ def initialize(name, item_proc, opts = {})
28
+ @name = name
29
+ @item_proc = item_proc
30
+ @items = []
31
+
32
+ setopts(opts, allowed_initialize_opts)
33
+ end
34
+
35
+ # Takes an array of key/value pairs and assigns each into a
36
+ # new instance created by invoking @item_proc.
37
+ #
38
+ # @param items [Array] List of items to add to this list.
39
+ def assign(items)
40
+ return if items.nil?
41
+
42
+ items.each do |values|
43
+ new_item = @item_proc.call
44
+ new_item.parent = self
45
+ new_item.assign(values)
46
+ @items << new_item
47
+ end
48
+ end
49
+
50
+ # Validate all items in the list and return validation errors
51
+ #
52
+ # @return [nil, Hash] If all validation passed nil will be returned; if
53
+ # validation failed then a hash of those errors will be returned.
54
+ def validate
55
+ errors = {}
56
+
57
+ @items.each_with_index do |item, idx|
58
+ begin
59
+ item.validate
60
+ rescue => error
61
+ errors[idx+1] = error
62
+ end
63
+ end
64
+
65
+ if !errors.empty?
66
+ raise ValidationError.new("Validation failed for '#{@name}' settings list", :errors => errors)
67
+ end
68
+ end
69
+
70
+ # Evaluate all items in the list and return a frozen array of the final values.
71
+ # @return [Array]
72
+ def resolve
73
+ @items.collect { |item| item.resolve }.freeze
74
+ end
75
+
76
+ class ValidationError < R10K::Error
77
+ attr_reader :errors
78
+
79
+ def initialize(mesg, options = {})
80
+ super
81
+ @errors = options[:errors]
82
+ end
83
+
84
+ def format
85
+ struct = []
86
+ struct << "#{message}:"
87
+ @errors.each do |item, error|
88
+ struct << indent(structure_exception("Item #{item}", error))
89
+ end
90
+ struct.join("\n")
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ # Subclasses may define additional params that are accepted at
97
+ # initialization; they should override this method to add any
98
+ # additional fields that should be respected.
99
+ def allowed_initialize_opts
100
+ {
101
+ :desc => true,
102
+ :default => true,
103
+ }
104
+ end
105
+ end
106
+ end
107
+ end