r10k 3.11.0 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docker.yml +1 -1
  3. data/.github/workflows/rspec_tests.yml +2 -2
  4. data/.travis.yml +1 -1
  5. data/CHANGELOG.mkd +34 -0
  6. data/doc/dynamic-environments/configuration.mkd +35 -7
  7. data/doc/dynamic-environments/usage.mkd +26 -0
  8. data/doc/puppetfile.mkd +16 -4
  9. data/integration/tests/basic_functionality/basic_deployment.rb +176 -0
  10. data/lib/r10k/action/base.rb +1 -1
  11. data/lib/r10k/action/deploy/deploy_helpers.rb +4 -0
  12. data/lib/r10k/action/deploy/display.rb +1 -1
  13. data/lib/r10k/action/deploy/environment.rb +13 -8
  14. data/lib/r10k/action/deploy/module.rb +14 -10
  15. data/lib/r10k/action/puppetfile/check.rb +7 -5
  16. data/lib/r10k/action/puppetfile/install.rb +22 -16
  17. data/lib/r10k/action/puppetfile/purge.rb +12 -9
  18. data/lib/r10k/action/runner.rb +6 -0
  19. data/lib/r10k/action/visitor.rb +3 -0
  20. data/lib/r10k/cli/deploy.rb +1 -0
  21. data/lib/r10k/cli/puppetfile.rb +0 -1
  22. data/lib/r10k/content_synchronizer.rb +16 -4
  23. data/lib/r10k/environment/bare.rb +4 -7
  24. data/lib/r10k/environment/base.rb +64 -11
  25. data/lib/r10k/environment/name.rb +14 -9
  26. data/lib/r10k/environment/plain.rb +16 -0
  27. data/lib/r10k/environment/tarball.rb +78 -0
  28. data/lib/r10k/environment/with_modules.rb +6 -10
  29. data/lib/r10k/environment.rb +2 -0
  30. data/lib/r10k/errors.rb +5 -0
  31. data/lib/r10k/forge/module_release.rb +2 -1
  32. data/lib/r10k/git/cache.rb +4 -13
  33. data/lib/r10k/git/rugged/base_repository.rb +12 -1
  34. data/lib/r10k/git/rugged/cache.rb +8 -0
  35. data/lib/r10k/git/stateful_repository.rb +9 -0
  36. data/lib/r10k/initializers.rb +21 -7
  37. data/lib/r10k/logging.rb +78 -1
  38. data/lib/r10k/module/base.rb +5 -1
  39. data/lib/r10k/module/definition.rb +64 -0
  40. data/lib/r10k/module/forge.rb +10 -2
  41. data/lib/r10k/module/git.rb +22 -1
  42. data/lib/r10k/module/local.rb +6 -3
  43. data/lib/r10k/module/svn.rb +10 -0
  44. data/lib/r10k/module/tarball.rb +101 -0
  45. data/lib/r10k/module.rb +21 -2
  46. data/lib/r10k/module_loader/puppetfile/dsl.rb +8 -3
  47. data/lib/r10k/module_loader/puppetfile.rb +109 -30
  48. data/lib/r10k/puppetfile.rb +1 -2
  49. data/lib/r10k/settings.rb +46 -0
  50. data/lib/r10k/source/git.rb +18 -18
  51. data/lib/r10k/source/yaml.rb +1 -1
  52. data/lib/r10k/tarball.rb +183 -0
  53. data/lib/r10k/util/cacheable.rb +31 -0
  54. data/lib/r10k/util/downloader.rb +134 -0
  55. data/lib/r10k/util/purgeable.rb +6 -2
  56. data/lib/r10k/util/setopts.rb +2 -0
  57. data/lib/r10k/util/subprocess.rb +1 -0
  58. data/lib/r10k/version.rb +1 -1
  59. data/locales/r10k.pot +119 -71
  60. data/r10k.gemspec +2 -2
  61. data/r10k.yaml.example +28 -0
  62. data/spec/fixtures/tarball/tarball.tar.gz +0 -0
  63. data/spec/fixtures/unit/action/r10k_logging.yaml +12 -0
  64. data/spec/fixtures/unit/puppetfile/forge-override/Puppetfile +8 -0
  65. data/spec/fixtures/unit/puppetfile/various-modules/Puppetfile +10 -0
  66. data/spec/fixtures/unit/puppetfile/various-modules/Puppetfile.new +10 -0
  67. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/managed_symlink_file +1 -0
  68. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/{managed_subdir_2 → subdir_allowlisted_2}/ignored_1 +0 -0
  69. data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/unmanaged_symlink_dir +1 -0
  70. data/spec/fixtures/unit/util/purgeable/managed_one/managed_symlink_dir +1 -0
  71. data/spec/fixtures/unit/util/purgeable/managed_one/unmanaged_symlink_file +1 -0
  72. data/spec/integration/git/rugged/cache_spec.rb +33 -0
  73. data/spec/integration/util/purageable_spec.rb +41 -0
  74. data/spec/r10k-mocks/mock_env.rb +3 -0
  75. data/spec/r10k-mocks/mock_source.rb +7 -3
  76. data/spec/shared-contexts/tarball.rb +32 -0
  77. data/spec/spec_helper.rb +1 -0
  78. data/spec/unit/action/deploy/environment_spec.rb +80 -30
  79. data/spec/unit/action/deploy/module_spec.rb +52 -64
  80. data/spec/unit/action/puppetfile/check_spec.rb +17 -5
  81. data/spec/unit/action/puppetfile/install_spec.rb +42 -36
  82. data/spec/unit/action/puppetfile/purge_spec.rb +15 -17
  83. data/spec/unit/action/runner_spec.rb +52 -9
  84. data/spec/unit/environment/bare_spec.rb +13 -0
  85. data/spec/unit/environment/base_spec.rb +30 -17
  86. data/spec/unit/environment/git_spec.rb +2 -2
  87. data/spec/unit/environment/name_spec.rb +18 -0
  88. data/spec/unit/environment/plain_spec.rb +8 -0
  89. data/spec/unit/environment/svn_spec.rb +4 -3
  90. data/spec/unit/environment/tarball_spec.rb +45 -0
  91. data/spec/unit/environment/with_modules_spec.rb +3 -2
  92. data/spec/unit/git/cache_spec.rb +2 -15
  93. data/spec/unit/git/rugged/cache_spec.rb +19 -0
  94. data/spec/unit/module/base_spec.rb +8 -8
  95. data/spec/unit/module/forge_spec.rb +32 -4
  96. data/spec/unit/module/git_spec.rb +51 -10
  97. data/spec/unit/module/svn_spec.rb +18 -6
  98. data/spec/unit/module/tarball_spec.rb +70 -0
  99. data/spec/unit/module_loader/puppetfile_spec.rb +96 -31
  100. data/spec/unit/puppetfile_spec.rb +2 -2
  101. data/spec/unit/settings_spec.rb +25 -2
  102. data/spec/unit/tarball_spec.rb +57 -0
  103. data/spec/unit/util/cacheable_spec.rb +23 -0
  104. data/spec/unit/util/downloader_spec.rb +98 -0
  105. data/spec/unit/util/purgeable_spec.rb +22 -11
  106. metadata +44 -17
@@ -35,6 +35,7 @@ class R10K::Git::StatefulRepository
35
35
  @cache.resolve(ref)
36
36
  end
37
37
 
38
+ # Returns true if the sync actually updated the repo, false otherwise
38
39
  def sync(ref, force=true)
39
40
  @cache.sync if sync_cache?(ref)
40
41
 
@@ -46,6 +47,7 @@ class R10K::Git::StatefulRepository
46
47
 
47
48
  workdir_status = status(ref)
48
49
 
50
+ updated = true
49
51
  case workdir_status
50
52
  when :absent
51
53
  logger.debug(_("Cloning %{repo_path} and checking out %{ref}") % {repo_path: @repo.path, ref: ref })
@@ -64,21 +66,28 @@ class R10K::Git::StatefulRepository
64
66
  @repo.checkout(sha, {:force => force})
65
67
  else
66
68
  logger.warn(_("Skipping %{repo_path} due to local modifications") % {repo_path: @repo.path})
69
+ updated = false
67
70
  end
68
71
  else
69
72
  logger.debug(_("%{repo_path} is already at Git ref %{ref}") % {repo_path: @repo.path, ref: ref })
73
+ updated = false
70
74
  end
75
+ updated
71
76
  end
72
77
 
73
78
  def status(ref)
74
79
  if !@repo.exist?
75
80
  :absent
81
+ elsif !@cache.exist?
82
+ :mismatched
76
83
  elsif !@repo.git_dir.exist?
77
84
  :mismatched
78
85
  elsif !@repo.git_dir.directory?
79
86
  :mismatched
80
87
  elsif !(@repo.origin == @remote)
81
88
  :mismatched
89
+ elsif @repo.head.nil?
90
+ :mismatched
82
91
  elsif @repo.dirty?
83
92
  :dirty
84
93
  elsif !(@repo.head == @cache.resolve(ref))
@@ -4,6 +4,7 @@ require 'r10k/git'
4
4
  require 'r10k/git/cache'
5
5
 
6
6
  require 'r10k/forge/module_release'
7
+ require 'r10k/tarball'
7
8
 
8
9
  module R10K
9
10
  module Initializers
@@ -30,14 +31,27 @@ module R10K
30
31
  logger.warn(_("the purgedirs key in r10k.yaml is deprecated. it is currently ignored."))
31
32
  end
32
33
 
34
+ with_setting(:logging) { |value| LoggingInitializer.new(value).call }
35
+
33
36
  with_setting(:deploy) { |value| DeployInitializer.new(value).call }
34
37
 
35
38
  with_setting(:cachedir) { |value| R10K::Git::Cache.settings[:cache_root] = value }
36
39
  with_setting(:cachedir) { |value| R10K::Forge::ModuleRelease.settings[:cache_root] = value }
40
+ with_setting(:cachedir) { |value| R10K::Tarball.settings[:cache_root] = value }
37
41
  with_setting(:pool_size) { |value| R10K::Puppetfile.settings[:pool_size] = value }
42
+ with_setting(:proxy) { |value| R10K::Tarball.settings[:proxy] = value }
38
43
 
39
44
  with_setting(:git) { |value| GitInitializer.new(value).call }
40
45
  with_setting(:forge) { |value| ForgeInitializer.new(value).call }
46
+ with_setting(:tarball) { |value| TarballInitializer.new(value).call }
47
+ end
48
+ end
49
+
50
+ class LoggingInitializer < BaseInitializer
51
+ def call
52
+ with_setting(:level) { |value| R10K::Logging.level = value }
53
+ with_setting(:disable_default_stderr) { |value| R10K::Logging.disable_default_stderr = value }
54
+ with_setting(:outputs) { |value| R10K::Logging.add_outputters(value) }
41
55
  end
42
56
  end
43
57
 
@@ -66,13 +80,13 @@ module R10K
66
80
  def call
67
81
  with_setting(:baseurl) { |value| PuppetForge.host = value }
68
82
  with_setting(:proxy) { |value| PuppetForge::Connection.proxy = value }
69
- with_setting(:authorization_token) { |value|
70
- if @settings[:baseurl]
71
- PuppetForge::Connection.authorization = value
72
- else
73
- raise R10K::Error, "Cannot specify a Forge authorization token without configuring a custom baseurl."
74
- end
75
- }
83
+ with_setting(:authorization_token) { |value| PuppetForge::Connection.authorization = value }
84
+ end
85
+ end
86
+
87
+ class TarballInitializer < BaseInitializer
88
+ def call
89
+ with_setting(:proxy) { |value| R10K::Tarball.settings[:proxy] = value }
76
90
  end
77
91
  end
78
92
  end
data/lib/r10k/logging.rb CHANGED
@@ -8,6 +8,16 @@ require 'r10k/logging/terminaloutputter'
8
8
  module R10K::Logging
9
9
 
10
10
  LOG_LEVELS = %w{DEBUG2 DEBUG1 DEBUG INFO NOTICE WARN ERROR FATAL}
11
+ SYSLOG_LEVELS_MAP = {
12
+ 'DEBUG2' => 'DEBUG',
13
+ 'DEBUG1' => 'DEBUG',
14
+ 'DEBUG' => 'DEBUG',
15
+ 'INFO' => 'INFO',
16
+ 'NOTICE' => 'INFO',
17
+ 'WARN' => 'WARN',
18
+ 'ERROR' => 'ERROR',
19
+ 'FATAL' => 'FATAL',
20
+ }.freeze
11
21
 
12
22
  def logger_name
13
23
  self.class.to_s
@@ -21,6 +31,9 @@ module R10K::Logging
21
31
  else
22
32
  @logger = Log4r::Logger.new(name)
23
33
  @logger.add(R10K::Logging.outputter)
34
+ R10K::Logging.outputters.each do |output|
35
+ @logger.add(output)
36
+ end
24
37
  end
25
38
  end
26
39
  @logger
@@ -59,7 +72,7 @@ module R10K::Logging
59
72
  if level.nil?
60
73
  raise ArgumentError, _("Invalid log level '%{val}'. Valid levels are %{log_levels}") % {val: val, log_levels: LOG_LEVELS.map(&:downcase).inspect}
61
74
  end
62
- outputter.level = level
75
+ outputter.level = level unless @disable_default_stderr
63
76
  @level = level
64
77
 
65
78
  if level < Log4r::INFO
@@ -69,6 +82,58 @@ module R10K::Logging
69
82
  end
70
83
  end
71
84
 
85
+ def disable_default_stderr=(val)
86
+ @disable_default_stderr = val
87
+ outputter.level = val ? Log4r::OFF : @level
88
+ end
89
+
90
+ def add_outputters(outputs)
91
+ outputs.each do |output|
92
+ type = output.fetch(:type)
93
+ # Support specifying both short as well as full names
94
+ type = type.to_s[0..-10] if type.to_s.downcase.end_with? 'outputter'
95
+
96
+ name = output.fetch(:name, 'r10k')
97
+ if output[:level]
98
+ level = parse_level(output[:level])
99
+ if level.nil?
100
+ raise ArgumentError, _("Invalid log level '%{val}'. Valid levels are %{log_levels}") % { val: output[:level], log_levels: LOG_LEVELS.map(&:downcase).inspect }
101
+ end
102
+ else
103
+ level = self.level
104
+ end
105
+ only_at = output[:only_at]
106
+ only_at&.map! do |val|
107
+ lv = parse_level(val)
108
+ if lv.nil?
109
+ raise ArgumentError, _("Invalid log level '%{val}'. Valid levels are %{log_levels}") % { val: val, log_levels: LOG_LEVELS.map(&:downcase).inspect }
110
+ end
111
+
112
+ lv
113
+ end
114
+ parameters = output.fetch(:parameters, {}).merge({ level: level })
115
+
116
+ begin
117
+ # Try to load the outputter file if possible
118
+ require "log4r/outputter/#{type.to_s.downcase}outputter"
119
+ rescue LoadError
120
+ false
121
+ end
122
+ outputtertype = Log4r.constants
123
+ .select { |klass| klass.to_s.end_with? 'Outputter' }
124
+ .find { |klass| klass.to_s.downcase == "#{type.to_s.downcase}outputter" }
125
+ raise ArgumentError, "Unable to find a #{output[:type]} outputter." unless outputtertype
126
+
127
+ outputter = Log4r.const_get(outputtertype).new(name, parameters)
128
+ outputter.only_at(*only_at) if only_at
129
+ # Handle log4r's syslog mapping correctly
130
+ outputter.map_levels_by_name_to_syslog(SYSLOG_LEVELS_MAP) if outputter.respond_to? :map_levels_by_name_to_syslog
131
+
132
+ @outputters << outputter
133
+ Log4r::Logger.global.add outputter
134
+ end
135
+ end
136
+
72
137
  extend Forwardable
73
138
  def_delegators :@outputter, :use_color, :use_color=
74
139
 
@@ -87,6 +152,16 @@ module R10K::Logging
87
152
  # @return [Log4r::Outputter]
88
153
  attr_reader :outputter
89
154
 
155
+ # @!attribute [r] outputters
156
+ # @api private
157
+ # @return [Array[Log4r::Outputter]]
158
+ attr_reader :outputters
159
+
160
+ # @!attribute [r] disable_default_stderr
161
+ # @api private
162
+ # @return [Boolean]
163
+ attr_reader :disable_default_stderr
164
+
90
165
  def default_formatter
91
166
  Log4r::PatternFormatter.new(:pattern => '%l\t -> %m')
92
167
  end
@@ -106,4 +181,6 @@ module R10K::Logging
106
181
  @level = Log4r::WARN
107
182
  @formatter = default_formatter
108
183
  @outputter = default_outputter
184
+ @outputters = []
185
+ @disable_default_stderr = false
109
186
  end
@@ -1,9 +1,12 @@
1
1
  require 'r10k/module'
2
+ require 'r10k/logging'
2
3
  require 'puppet_forge'
3
4
 
4
5
  # This class defines a common interface for module implementations.
5
6
  class R10K::Module::Base
6
7
 
8
+ include R10K::Logging
9
+
7
10
  # @!attribute [r] title
8
11
  # @return [String] The forward slash separated owner and name of the module
9
12
  attr_reader :title
@@ -47,7 +50,7 @@ class R10K::Module::Base
47
50
 
48
51
  # @param title [String]
49
52
  # @param dirname [String]
50
- # @param args [Array]
53
+ # @param args [Hash]
51
54
  def initialize(title, dirname, args, environment=nil)
52
55
  @title = PuppetForge::V3.normalize_name(title)
53
56
  @dirname = dirname
@@ -103,6 +106,7 @@ class R10K::Module::Base
103
106
 
104
107
  # Synchronize this module with the indicated state.
105
108
  # @param [Hash] opts Deprecated
109
+ # @return [Boolean] true if the module was updated, false otherwise
106
110
  def sync(opts={})
107
111
  raise NotImplementedError
108
112
  end
@@ -0,0 +1,64 @@
1
+ require 'r10k/module'
2
+
3
+ class R10K::Module::Definition < R10K::Module::Base
4
+
5
+ attr_reader :version
6
+
7
+ def initialize(name, dirname:, args:, implementation:, environment: nil)
8
+ @original_name = name
9
+ @original_args = args.dup
10
+ @implementation = implementation
11
+ @version = implementation.statically_defined_version(name, args)
12
+
13
+ super(name, dirname, args, environment)
14
+ end
15
+
16
+ def to_implementation
17
+ mod = @implementation.new(@title, @dirname, @original_args, @environment)
18
+
19
+ mod.origin = origin
20
+ mod.spec_deletable = spec_deletable
21
+
22
+ mod
23
+ end
24
+
25
+ # syncing is a noop for module definitions
26
+ # Returns false to inidicate the module was not updated
27
+ def sync(args = {})
28
+ logger.debug1(_("Not updating module %{name}, assuming content unchanged") % {name: name})
29
+ false
30
+ end
31
+
32
+ def status
33
+ :insync
34
+ end
35
+
36
+ def properties
37
+ type = nil
38
+
39
+ if @args[:type]
40
+ type = @args[:type]
41
+ elsif @args[:ref] || @args[:commit] || @args[:branch] || @args[:tag]
42
+ type = 'git'
43
+ elsif @args[:svn]
44
+ # This logic is clear and included for completeness sake, though at
45
+ # this time module definitions do not support SVN versions.
46
+ type = 'svn'
47
+ else
48
+ type = 'forge'
49
+ end
50
+
51
+ {
52
+ expected: version,
53
+ # We can't get the value for `actual` here because that requires the
54
+ # implementation (and potentially expensive operations by the
55
+ # implementation). Some consumers will check this value, if it exists
56
+ # and if not, fall back to the expected version. That is the correct
57
+ # behavior when assuming modules are unchanged, and why `actual` is set
58
+ # to `nil` here.
59
+ actual: nil,
60
+ type: type
61
+ }
62
+ end
63
+ end
64
+
@@ -25,6 +25,10 @@ class R10K::Module::Forge < R10K::Module::Base
25
25
  expected_version == :latest || expected_version.nil? || PuppetForge::Util.version_valid?(expected_version)
26
26
  end
27
27
 
28
+ def self.statically_defined_version(name, args)
29
+ args[:version] if args[:version].is_a?(String)
30
+ end
31
+
28
32
  # @!attribute [r] metadata
29
33
  # @api private
30
34
  # @return [PuppetForge::Metadata]
@@ -35,8 +39,6 @@ class R10K::Module::Forge < R10K::Module::Base
35
39
  # @return [PuppetForge::V3::Module] The Puppet Forge module metadata
36
40
  attr_reader :v3_module
37
41
 
38
- include R10K::Logging
39
-
40
42
  include R10K::Util::Setopts
41
43
 
42
44
  def initialize(title, dirname, opts, environment=nil)
@@ -58,18 +60,24 @@ class R10K::Module::Forge < R10K::Module::Base
58
60
  end
59
61
 
60
62
  # @param [Hash] opts Deprecated
63
+ # @return [Boolean] true if the module was updated, false otherwise
61
64
  def sync(opts={})
65
+ updated = false
62
66
  if should_sync?
63
67
  case status
64
68
  when :absent
65
69
  install
70
+ updated = true
66
71
  when :outdated
67
72
  upgrade
73
+ updated = true
68
74
  when :mismatched
69
75
  reinstall
76
+ updated = true
70
77
  end
71
78
  maybe_delete_spec_dir
72
79
  end
80
+ updated
73
81
  end
74
82
 
75
83
  def properties
@@ -13,6 +13,21 @@ class R10K::Module::Git < R10K::Module::Base
13
13
  false
14
14
  end
15
15
 
16
+ # Will be called if self.implement? above returns true. Will return
17
+ # the version info, if version is statically defined in the modules
18
+ # declaration.
19
+ def self.statically_defined_version(name, args)
20
+ if !args[:type] && (args[:ref] || args[:tag] || args[:commit])
21
+ if args[:ref] && args[:ref].to_s.match(/[0-9a-f]{40}/)
22
+ args[:ref]
23
+ else
24
+ args[:tag] || args[:commit]
25
+ end
26
+ elsif args[:type].to_s == 'git' && args[:version] && args[:version].to_s.match(/[0-9a-f]{40}/)
27
+ args[:version]
28
+ end
29
+ end
30
+
16
31
  # @!attribute [r] repo
17
32
  # @api private
18
33
  # @return [R10K::Git::StatefulRepository]
@@ -83,10 +98,16 @@ class R10K::Module::Git < R10K::Module::Base
83
98
  end
84
99
 
85
100
  # @param [Hash] opts Deprecated
101
+ # @return [Boolean] true if the module was updated, false otherwise
86
102
  def sync(opts={})
87
103
  force = opts[:force] || @force
88
- @repo.sync(version, force) if should_sync?
104
+ if should_sync?
105
+ updated = @repo.sync(version, force)
106
+ else
107
+ updated = false
108
+ end
89
109
  maybe_delete_spec_dir
110
+ updated
90
111
  end
91
112
 
92
113
  def status
@@ -1,5 +1,4 @@
1
1
  require 'r10k/module'
2
- require 'r10k/logging'
3
2
 
4
3
  # A dummy module type that can be used to "protect" Puppet modules that exist
5
4
  # inside of the Puppetfile "moduledir" location. Local modules will not be
@@ -12,10 +11,12 @@ class R10K::Module::Local < R10K::Module::Base
12
11
  args.is_a?(Hash) && (args[:local] || args[:type].to_s == 'local')
13
12
  end
14
13
 
15
- include R10K::Logging
14
+ def self.statically_defined_version(*)
15
+ "0.0.0"
16
+ end
16
17
 
17
18
  def version
18
- "0.0.0"
19
+ self.class.statically_defined_version
19
20
  end
20
21
 
21
22
  def properties
@@ -31,7 +32,9 @@ class R10K::Module::Local < R10K::Module::Base
31
32
  end
32
33
 
33
34
  # @param [Hash] opts Deprecated
35
+ # @return [Boolean] false, because local modules are always considered in-sync
34
36
  def sync(opts={})
35
37
  logger.debug1 _("Module %{title} is a local module, always indicating synced.") % {title: title}
38
+ false
36
39
  end
37
40
  end
@@ -10,6 +10,10 @@ class R10K::Module::SVN < R10K::Module::Base
10
10
  args.has_key?(:svn) || args[:type].to_s == 'svn'
11
11
  end
12
12
 
13
+ def self.statically_defined_version(name, args)
14
+ nil
15
+ end
16
+
13
17
  # @!attribute [r] expected_revision
14
18
  # @return [String] The SVN revision that the repo should have checked out
15
19
  attr_reader :expected_revision
@@ -70,18 +74,24 @@ class R10K::Module::SVN < R10K::Module::Base
70
74
  end
71
75
 
72
76
  # @param [Hash] opts Deprecated
77
+ # @return [Boolean] true if the module was updated, false otherwise
73
78
  def sync(opts={})
79
+ updated = false
74
80
  if should_sync?
75
81
  case status
76
82
  when :absent
77
83
  install
84
+ updated = true
78
85
  when :mismatched
79
86
  reinstall
87
+ updated = true
80
88
  when :outdated
81
89
  update
90
+ updated = true
82
91
  end
83
92
  maybe_delete_spec_dir
84
93
  end
94
+ updated
85
95
  end
86
96
 
87
97
  def exist?
@@ -0,0 +1,101 @@
1
+ require 'r10k/module'
2
+ require 'r10k/util/setopts'
3
+ require 'r10k/tarball'
4
+
5
+ # This class defines a tarball source module implementation
6
+ class R10K::Module::Tarball < R10K::Module::Base
7
+
8
+ R10K::Module.register(self)
9
+
10
+ def self.implement?(name, args)
11
+ args.is_a?(Hash) && args[:type].to_s == 'tarball'
12
+ rescue
13
+ false
14
+ end
15
+
16
+ def self.statically_defined_version(name, args)
17
+ args[:version] || args[:checksum]
18
+ end
19
+
20
+ # @!attribute [r] tarball
21
+ # @api private
22
+ # @return [R10K::Tarball]
23
+ attr_reader :tarball
24
+
25
+ include R10K::Util::Setopts
26
+
27
+ def initialize(name, dirname, opts, environment=nil)
28
+ super
29
+ setopts(opts, {
30
+ # Standard option interface
31
+ :source => :self,
32
+ :version => :checksum,
33
+ :type => ::R10K::Util::Setopts::Ignore,
34
+ :overrides => :self,
35
+
36
+ # Type-specific options
37
+ :checksum => :self,
38
+ })
39
+
40
+ @tarball = R10K::Tarball.new(name, @source, checksum: @checksum)
41
+ end
42
+
43
+ # Return the status of the currently installed module.
44
+ #
45
+ # @return [Symbol]
46
+ def status
47
+ if not path.exist?
48
+ :absent
49
+ elsif not (tarball.cache_valid? && tarball.insync?(path.to_s))
50
+ :mismatched
51
+ else
52
+ :insync
53
+ end
54
+ end
55
+
56
+ # Synchronize this module with the indicated state.
57
+ # @param [Hash] opts Deprecated
58
+ # @return [Boolean] true if the module was updated, false otherwise
59
+ def sync(opts={})
60
+ tarball.get unless tarball.cache_valid?
61
+ if should_sync?
62
+ case status
63
+ when :absent
64
+ tarball.unpack(path.to_s)
65
+ when :mismatched
66
+ path.rmtree
67
+ tarball.unpack(path.to_s)
68
+ end
69
+ maybe_delete_spec_dir
70
+ true
71
+ else
72
+ false
73
+ end
74
+ end
75
+
76
+ # Return the desired version of this module
77
+ def version
78
+ @checksum || '(present)'
79
+ end
80
+
81
+ # Return the properties of the module
82
+ #
83
+ # @return [Hash]
84
+ # @abstract
85
+ def properties
86
+ {
87
+ :expected => version,
88
+ :actual => ((state = status) == :insync) ? version : state,
89
+ :type => :tarball,
90
+ }
91
+ end
92
+
93
+ # Tarball caches are files, not directories. An important purpose of this
94
+ # method is to indicate where the cache "path" is, for locking/parallelism,
95
+ # so for the Tarball module type, the relevant path location is returned.
96
+ #
97
+ # @return [String] The path this module will cache its tarball source to
98
+ def cachedir
99
+ tarball.cache_path
100
+ end
101
+ end
data/lib/r10k/module.rb CHANGED
@@ -22,17 +22,36 @@ module R10K::Module
22
22
  #
23
23
  # @return [Object < R10K::Module] A member of the implementing subclass
24
24
  def self.new(name, basedir, args, environment=nil)
25
+ with_implementation(name, args) do |implementation|
26
+ implementation.new(name, basedir, args, environment)
27
+ end
28
+ end
29
+
30
+ # Takes the same signature as Module.new but returns an metadata module
31
+ def self.from_metadata(name, basedir, args, environment=nil)
32
+ with_implementation(name, args) do |implementation|
33
+ R10K::Module::Definition.new(name,
34
+ dirname: basedir,
35
+ args: args,
36
+ implementation: implementation,
37
+ environment: environment)
38
+ end
39
+ end
40
+
41
+ def self.with_implementation(name, args, &block)
25
42
  if implementation = @klasses.find { |klass| klass.implement?(name, args) }
26
- obj = implementation.new(name, basedir, args, environment)
27
- obj
43
+ block.call(implementation)
28
44
  else
29
45
  raise _("Module %{name} with args %{args} doesn't have an implementation. (Are you using the right arguments?)") % {name: name, args: args.inspect}
30
46
  end
31
47
  end
32
48
 
49
+
33
50
  require 'r10k/module/base'
34
51
  require 'r10k/module/git'
35
52
  require 'r10k/module/svn'
36
53
  require 'r10k/module/local'
37
54
  require 'r10k/module/forge'
55
+ require 'r10k/module/definition'
56
+ require 'r10k/module/tarball'
38
57
  end
@@ -6,8 +6,9 @@ module R10K
6
6
  #
7
7
  # @api private
8
8
 
9
- def initialize(librarian)
10
- @librarian = librarian
9
+ def initialize(librarian, metadata_only: false)
10
+ @librarian = librarian
11
+ @metadata_only = metadata_only
11
12
  end
12
13
 
13
14
  def mod(name, args = nil)
@@ -17,7 +18,11 @@ module R10K
17
18
  opts = { version: args }
18
19
  end
19
20
 
20
- @librarian.add_module(name, opts)
21
+ if @metadata_only
22
+ @librarian.add_module_metadata(name, opts)
23
+ else
24
+ @librarian.add_module(name, opts)
25
+ end
21
26
  end
22
27
 
23
28
  def forge(location)