pdk-akerl 1.8.0.1

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 (81) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +826 -0
  3. data/LICENSE +201 -0
  4. data/README.md +133 -0
  5. data/exe/pdk +10 -0
  6. data/lib/pdk.rb +10 -0
  7. data/lib/pdk/answer_file.rb +121 -0
  8. data/lib/pdk/cli.rb +113 -0
  9. data/lib/pdk/cli/build.rb +76 -0
  10. data/lib/pdk/cli/bundle.rb +42 -0
  11. data/lib/pdk/cli/convert.rb +41 -0
  12. data/lib/pdk/cli/errors.rb +23 -0
  13. data/lib/pdk/cli/exec.rb +246 -0
  14. data/lib/pdk/cli/exec_group.rb +67 -0
  15. data/lib/pdk/cli/module.rb +14 -0
  16. data/lib/pdk/cli/module/build.rb +14 -0
  17. data/lib/pdk/cli/module/generate.rb +45 -0
  18. data/lib/pdk/cli/new.rb +17 -0
  19. data/lib/pdk/cli/new/class.rb +32 -0
  20. data/lib/pdk/cli/new/defined_type.rb +30 -0
  21. data/lib/pdk/cli/new/module.rb +41 -0
  22. data/lib/pdk/cli/new/provider.rb +27 -0
  23. data/lib/pdk/cli/new/task.rb +31 -0
  24. data/lib/pdk/cli/test.rb +12 -0
  25. data/lib/pdk/cli/test/unit.rb +88 -0
  26. data/lib/pdk/cli/update.rb +32 -0
  27. data/lib/pdk/cli/util.rb +193 -0
  28. data/lib/pdk/cli/util/command_redirector.rb +26 -0
  29. data/lib/pdk/cli/util/interview.rb +63 -0
  30. data/lib/pdk/cli/util/option_normalizer.rb +53 -0
  31. data/lib/pdk/cli/util/option_validator.rb +56 -0
  32. data/lib/pdk/cli/validate.rb +124 -0
  33. data/lib/pdk/generate.rb +11 -0
  34. data/lib/pdk/generate/defined_type.rb +49 -0
  35. data/lib/pdk/generate/module.rb +318 -0
  36. data/lib/pdk/generate/provider.rb +82 -0
  37. data/lib/pdk/generate/puppet_class.rb +48 -0
  38. data/lib/pdk/generate/puppet_object.rb +288 -0
  39. data/lib/pdk/generate/task.rb +86 -0
  40. data/lib/pdk/i18n.rb +4 -0
  41. data/lib/pdk/logger.rb +28 -0
  42. data/lib/pdk/module.rb +21 -0
  43. data/lib/pdk/module/build.rb +214 -0
  44. data/lib/pdk/module/convert.rb +209 -0
  45. data/lib/pdk/module/metadata.rb +193 -0
  46. data/lib/pdk/module/templatedir.rb +313 -0
  47. data/lib/pdk/module/update.rb +111 -0
  48. data/lib/pdk/module/update_manager.rb +210 -0
  49. data/lib/pdk/report.rb +112 -0
  50. data/lib/pdk/report/event.rb +357 -0
  51. data/lib/pdk/template_file.rb +89 -0
  52. data/lib/pdk/tests/unit.rb +213 -0
  53. data/lib/pdk/util.rb +271 -0
  54. data/lib/pdk/util/bundler.rb +253 -0
  55. data/lib/pdk/util/filesystem.rb +12 -0
  56. data/lib/pdk/util/git.rb +74 -0
  57. data/lib/pdk/util/puppet_version.rb +242 -0
  58. data/lib/pdk/util/ruby_version.rb +147 -0
  59. data/lib/pdk/util/vendored_file.rb +88 -0
  60. data/lib/pdk/util/version.rb +42 -0
  61. data/lib/pdk/util/windows.rb +13 -0
  62. data/lib/pdk/util/windows/api_types.rb +57 -0
  63. data/lib/pdk/util/windows/file.rb +36 -0
  64. data/lib/pdk/util/windows/string.rb +16 -0
  65. data/lib/pdk/validate.rb +14 -0
  66. data/lib/pdk/validate/base_validator.rb +209 -0
  67. data/lib/pdk/validate/metadata/metadata_json_lint.rb +86 -0
  68. data/lib/pdk/validate/metadata/metadata_syntax.rb +109 -0
  69. data/lib/pdk/validate/metadata_validator.rb +30 -0
  70. data/lib/pdk/validate/puppet/puppet_lint.rb +67 -0
  71. data/lib/pdk/validate/puppet/puppet_syntax.rb +112 -0
  72. data/lib/pdk/validate/puppet_validator.rb +30 -0
  73. data/lib/pdk/validate/ruby/rubocop.rb +77 -0
  74. data/lib/pdk/validate/ruby_validator.rb +29 -0
  75. data/lib/pdk/validate/tasks/metadata_lint.rb +126 -0
  76. data/lib/pdk/validate/tasks/name.rb +88 -0
  77. data/lib/pdk/validate/tasks_validator.rb +33 -0
  78. data/lib/pdk/version.rb +4 -0
  79. data/locales/config.yaml +21 -0
  80. data/locales/pdk.pot +1283 -0
  81. metadata +304 -0
@@ -0,0 +1,253 @@
1
+ require 'bundler'
2
+ require 'digest'
3
+ require 'fileutils'
4
+ require 'pdk/util'
5
+ require 'pdk/cli/exec'
6
+
7
+ module PDK
8
+ module Util
9
+ module Bundler
10
+ class BundleHelper; end
11
+
12
+ def self.ensure_bundle!(gem_overrides = nil)
13
+ bundle = BundleHelper.new
14
+
15
+ # This will default ensure_bundle! to re-resolving everything to latest
16
+ gem_overrides ||= { puppet: nil, hiera: nil, facter: nil }
17
+
18
+ if already_bundled?(bundle.gemfile, gem_overrides)
19
+ PDK.logger.debug(_('Bundler managed gems already up to date.'))
20
+ return
21
+ end
22
+
23
+ unless bundle.gemfile?
24
+ PDK.logger.debug(_("No Gemfile found in '%{cwd}'. Skipping bundler management.") % { cwd: Dir.pwd })
25
+ return
26
+ end
27
+
28
+ unless bundle.locked?
29
+ # Generate initial default Gemfile.lock, either from package cache or
30
+ # by invoking `bundle lock`
31
+ bundle.lock!
32
+ end
33
+
34
+ # Check if all dependencies will be available once we update the lockfile.
35
+ begin
36
+ original_lockfile = bundle.gemfile_lock
37
+ temp_lockfile = "#{original_lockfile}.tmp"
38
+
39
+ FileUtils.mv(original_lockfile, temp_lockfile)
40
+
41
+ all_deps_available = bundle.installed?(gem_overrides)
42
+ ensure
43
+ FileUtils.mv(temp_lockfile, original_lockfile, force: true)
44
+ end
45
+
46
+ bundle.update_lock!(with: gem_overrides, local: all_deps_available)
47
+
48
+ # If there are missing dependencies after updating the lockfile, let `bundle install`
49
+ # go out and get them. If the specified puppet gem version points to a remote location
50
+ # or local filepath, then run bundle install as well.
51
+ if !bundle.installed? || (gem_overrides[:puppet] && gem_overrides[:puppet].start_with?('file://', 'git://', 'https://'))
52
+ bundle.install!(gem_overrides)
53
+ end
54
+
55
+ mark_as_bundled!(bundle.gemfile, gem_overrides)
56
+ end
57
+
58
+ def self.ensure_binstubs!(*gems)
59
+ bundle = BundleHelper.new
60
+
61
+ bundle.binstubs!(gems)
62
+ end
63
+
64
+ def self.already_bundled?(gemfile, gem_overrides)
65
+ !(@bundled ||= {})[bundle_cache_key(gemfile, gem_overrides)].nil?
66
+ end
67
+
68
+ def self.mark_as_bundled!(gemfile, gem_overrides)
69
+ (@bundled ||= {})[bundle_cache_key(gemfile, gem_overrides)] = true
70
+ end
71
+
72
+ def self.bundle_cache_key(gemfile, gem_overrides)
73
+ override_sig = (gem_overrides || {}).sort_by { |gem, _| gem.to_s }.to_s
74
+ Digest::MD5.hexdigest(gemfile.to_s + override_sig)
75
+ end
76
+ private_class_method :bundle_cache_key
77
+
78
+ class BundleHelper
79
+ def gemfile
80
+ @gemfile ||= PDK::Util.find_upwards('Gemfile')
81
+ end
82
+
83
+ def gemfile_lock
84
+ return nil if gemfile.nil?
85
+ @gemfile_lock ||= File.join(File.dirname(gemfile), 'Gemfile.lock')
86
+ end
87
+
88
+ def gemfile?
89
+ !gemfile.nil?
90
+ end
91
+
92
+ def locked?
93
+ !gemfile_lock.nil? && File.file?(gemfile_lock)
94
+ end
95
+
96
+ def installed?(gem_overrides = {})
97
+ PDK.logger.debug(_('Checking for missing Gemfile dependencies.'))
98
+
99
+ argv = ['check', "--gemfile=#{gemfile}", '--dry-run']
100
+
101
+ cmd = bundle_command(*argv).tap do |c|
102
+ c.update_environment(gemfile_env(gem_overrides)) unless gem_overrides.empty?
103
+ end
104
+
105
+ result = cmd.execute!
106
+
107
+ unless result[:exit_code].zero?
108
+ PDK.logger.debug(result.values_at(:stdout, :stderr).join("\n"))
109
+ end
110
+
111
+ result[:exit_code].zero?
112
+ end
113
+
114
+ def lock!
115
+ if PDK::Util.package_install?
116
+ # In packaged installs, use vendored Gemfile.lock as a starting point.
117
+ # Subsequent 'bundle install' will still pick up any new dependencies.
118
+ vendored_lockfiles = [
119
+ File.join(PDK::Util.package_cachedir, "Gemfile-#{PDK::Util::RubyVersion.active_ruby_version}.lock"),
120
+ File.join(PDK::Util.package_cachedir, 'Gemfile.lock'),
121
+ ]
122
+
123
+ vendored_gemfile_lock = vendored_lockfiles.find { |lockfile| File.exist?(lockfile) }
124
+
125
+ unless vendored_gemfile_lock
126
+ raise PDK::CLI::FatalError, _('Vendored Gemfile.lock (%{source}) not found.') % {
127
+ source: vendored_gemfile_lock,
128
+ }
129
+ end
130
+
131
+ PDK.logger.debug(_('Using vendored Gemfile.lock from %{source}.') % { source: vendored_gemfile_lock })
132
+ FileUtils.cp(vendored_gemfile_lock, File.join(PDK::Util.module_root, 'Gemfile.lock'))
133
+ else
134
+ argv = ['lock']
135
+
136
+ cmd = bundle_command(*argv).tap do |c|
137
+ c.add_spinner(_('Resolving default Gemfile dependencies.'))
138
+ end
139
+
140
+ result = cmd.execute!
141
+
142
+ unless result[:exit_code].zero?
143
+ PDK.logger.fatal(result.values_at(:stdout, :stderr).join("\n"))
144
+ raise PDK::CLI::FatalError, _('Unable to resolve default Gemfile dependencies.')
145
+ end
146
+
147
+ # After initial lockfile generation, re-resolve json gem to built-in
148
+ # version to avoid unncessary native compilation attempts. For packaged
149
+ # installs this is done during the generation of the vendored Gemfile.lock
150
+ update_lock!(only: { json: nil }, local: true)
151
+ end
152
+
153
+ true
154
+ end
155
+
156
+ def update_lock!(options = {})
157
+ PDK.logger.debug(_('Updating Gemfile dependencies.'))
158
+
159
+ argv = ['lock', "--lockfile=#{gemfile_lock}", '--update']
160
+
161
+ overrides = nil
162
+
163
+ if options && options[:only]
164
+ update_gems = options[:only].keys.map(&:to_s)
165
+ argv << update_gems
166
+ argv.flatten!
167
+
168
+ overrides = options[:only]
169
+ elsif options && options[:with]
170
+ overrides = options[:with]
171
+ end
172
+
173
+ argv << '--local' if options && options[:local]
174
+ argv << '--conservative' if options && options[:conservative]
175
+
176
+ cmd = bundle_command(*argv).tap do |c|
177
+ c.update_environment('BUNDLE_GEMFILE' => gemfile)
178
+ c.update_environment(gemfile_env(overrides)) if overrides
179
+ end
180
+
181
+ result = cmd.execute!
182
+
183
+ unless result[:exit_code].zero?
184
+ PDK.logger.fatal(result.values_at(:stdout, :stderr).join("\n"))
185
+ raise PDK::CLI::FatalError, _('Unable to resolve Gemfile dependencies.')
186
+ end
187
+
188
+ true
189
+ end
190
+
191
+ def install!(gem_overrides = {})
192
+ argv = ['install', "--gemfile=#{gemfile}"]
193
+ argv << '-j4' unless Gem.win_platform? && Gem::Version.new(PDK::Util::RubyVersion.active_ruby_version) < Gem::Version.new('2.3.5')
194
+
195
+ cmd = bundle_command(*argv).tap do |c|
196
+ c.add_spinner(_('Installing missing Gemfile dependencies.'))
197
+ c.update_environment(gemfile_env(gem_overrides)) unless gem_overrides.empty?
198
+ end
199
+
200
+ result = cmd.execute!
201
+
202
+ unless result[:exit_code].zero?
203
+ PDK.logger.fatal(result.values_at(:stdout, :stderr).join("\n"))
204
+ raise PDK::CLI::FatalError, _('Unable to install missing Gemfile dependencies.')
205
+ end
206
+
207
+ true
208
+ end
209
+
210
+ def binstubs!(gems)
211
+ binstub_dir = File.join(File.dirname(gemfile), 'bin')
212
+ return true if gems.all? { |gem| File.file?(File.join(binstub_dir, gem)) }
213
+
214
+ cmd = bundle_command('binstubs', *gems, '--force')
215
+ result = cmd.execute!
216
+
217
+ unless result[:exit_code].zero?
218
+ PDK.logger.fatal(_("Failed to generate binstubs for '%{gems}':\n%{output}") % { gems: gems.join(' '), output: result.values_at(:stdout, :stderr).join("\n") })
219
+ raise PDK::CLI::FatalError, _('Unable to install requested binstubs.')
220
+ end
221
+
222
+ true
223
+ end
224
+
225
+ def self.gemfile_env(gem_overrides)
226
+ gemfile_env = {}
227
+
228
+ return gemfile_env unless gem_overrides.respond_to?(:each)
229
+
230
+ gem_overrides.each do |gem, version|
231
+ gemfile_env['PUPPET_GEM_VERSION'] = version if gem.respond_to?(:to_s) && gem.to_s == 'puppet' && !version.nil?
232
+ gemfile_env['FACTER_GEM_VERSION'] = version if gem.respond_to?(:to_s) && gem.to_s == 'facter' && !version.nil?
233
+ gemfile_env['HIERA_GEM_VERSION'] = version if gem.respond_to?(:to_s) && gem.to_s == 'hiera' && !version.nil?
234
+ end
235
+
236
+ gemfile_env
237
+ end
238
+
239
+ private
240
+
241
+ def gemfile_env(gem_overrides)
242
+ self.class.gemfile_env(gem_overrides)
243
+ end
244
+
245
+ def bundle_command(*args)
246
+ PDK::CLI::Exec::Command.new(PDK::CLI::Exec.bundle_bin, *args).tap do |c|
247
+ c.context = :module
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,12 @@
1
+ module PDK
2
+ module Util
3
+ module Filesystem
4
+ def write_file(path, content)
5
+ raise ArgumentError unless path.is_a?(String) || path.respond_to?(:to_path)
6
+
7
+ File.open(path, 'wb') { |f| f.write(content.encode(universal_newline: true)) }
8
+ end
9
+ module_function :write_file
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,74 @@
1
+ module PDK
2
+ module Util
3
+ module Git
4
+ def self.git_bindir
5
+ @git_dir ||= File.join('private', 'git', Gem.win_platform? ? 'cmd' : 'bin')
6
+ end
7
+
8
+ def self.git_paths
9
+ @paths ||= begin
10
+ paths = [File.join(PDK::Util.pdk_package_basedir, git_bindir)]
11
+
12
+ if Gem.win_platform?
13
+ paths << File.join(PDK::Util.pdk_package_basedir, 'private', 'git', 'mingw64', 'bin')
14
+ paths << File.join(PDK::Util.pdk_package_basedir, 'private', 'git', 'mingw64', 'libexec', 'git-core')
15
+ paths << File.join(PDK::Util.pdk_package_basedir, 'private', 'git', 'usr', 'bin')
16
+ end
17
+
18
+ paths
19
+ end
20
+ end
21
+
22
+ def self.git_bin
23
+ git_bin = Gem.win_platform? ? 'git.exe' : 'git'
24
+ vendored_bin_path = File.join(git_bindir, git_bin)
25
+
26
+ PDK::CLI::Exec.try_vendored_bin(vendored_bin_path, git_bin)
27
+ end
28
+
29
+ def self.git(*args)
30
+ PDK::CLI::Exec.ensure_bin_present!(git_bin, 'git')
31
+
32
+ PDK::CLI::Exec.execute(git_bin, *args)
33
+ end
34
+
35
+ def self.git_with_env(env, *args)
36
+ PDK::CLI::Exec.ensure_bin_present!(git_bin, 'git')
37
+
38
+ PDK::CLI::Exec.execute_with_env(env, git_bin, *args)
39
+ end
40
+
41
+ def self.repo?(maybe_repo)
42
+ return bare_repo?(maybe_repo) if File.directory?(maybe_repo)
43
+
44
+ remote_repo?(maybe_repo)
45
+ end
46
+
47
+ def self.bare_repo?(maybe_repo)
48
+ env = { 'GIT_DIR' => maybe_repo }
49
+ rev_parse = git_with_env(env, 'rev-parse', '--is-bare-repository')
50
+
51
+ rev_parse[:exit_code].zero? && rev_parse[:stdout].strip == 'true'
52
+ end
53
+
54
+ def self.remote_repo?(maybe_repo)
55
+ git('ls-remote', '--exit-code', maybe_repo)[:exit_code].zero?
56
+ end
57
+
58
+ def self.ls_remote(repo, ref)
59
+ output = git('ls-remote', '--refs', repo, ref)
60
+
61
+ unless output[:exit_code].zero?
62
+ PDK.logger.error output[:stdout]
63
+ PDK.logger.error output[:stderr]
64
+ raise PDK::CLI::ExitWithError, _('Unable to access the template repository "%{repository}"') % {
65
+ repository: repo,
66
+ }
67
+ end
68
+
69
+ matching_refs = output[:stdout].split("\n").map { |r| r.split("\t") }
70
+ matching_refs.find { |_sha, remote_ref| remote_ref == ref }.first
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,242 @@
1
+ require 'pdk/util'
2
+ require 'pdk/util/git'
3
+
4
+ module PDK
5
+ module Util
6
+ class PuppetVersion
7
+ class << self
8
+ extend Forwardable
9
+
10
+ def_delegators :instance, :puppet_dev_env, :puppet_dev_path, :fetch_puppet_dev, :find_gem_for, :from_pe_version, :from_module_metadata, :latest_available
11
+
12
+ attr_writer :instance
13
+
14
+ def instance
15
+ @instance ||= new
16
+ end
17
+ end
18
+
19
+ PE_VERSIONS_URL = 'https://forgeapi.puppet.com/private/versions/pe'.freeze
20
+ DEFAULT_PUPPET_DEV_URL = 'https://github.com/puppetlabs/puppet'.freeze
21
+ DEFAULT_PUPPET_DEV_BRANCH = 'master'.freeze
22
+
23
+ def puppet_dev_env
24
+ {
25
+ gem_version: 'file://%{path}' % { path: puppet_dev_path },
26
+ ruby_version: PDK::Util::RubyVersion.latest_ruby_version,
27
+ }
28
+ end
29
+
30
+ def puppet_dev_path
31
+ File.join(PDK::Util.cachedir, 'src', 'puppet')
32
+ end
33
+
34
+ def latest_available
35
+ latest = find_gem(Gem::Requirement.create('>= 0'))
36
+
37
+ if latest.nil?
38
+ raise ArgumentError, _('Unable to find a Puppet gem in current Ruby environment or from Rubygems.org.')
39
+ end
40
+
41
+ latest
42
+ end
43
+
44
+ def fetch_puppet_dev
45
+ # Check if the source is cloned and is a readable git repo
46
+ unless PDK::Util::Git.remote_repo? puppet_dev_path
47
+ # Check if the path has something in it already. Delete it and prepare for clone if so.
48
+ if File.exist? puppet_dev_path
49
+ File.delete(puppet_dev_path) if File.file? puppet_dev_path
50
+ FileUtils.rm_rf(puppet_dev_path) if File.directory? puppet_dev_path
51
+ end
52
+
53
+ FileUtils.mkdir_p puppet_dev_path
54
+ clone_result = PDK::Util::Git.git('clone', DEFAULT_PUPPET_DEV_URL, puppet_dev_path)
55
+ return if clone_result[:exit_code].zero?
56
+
57
+ PDK.logger.error clone_result[:stdout]
58
+ PDK.logger.error clone_result[:stderr]
59
+ raise PDK::CLI::FatalError, _("Unable to clone git repository at '%{repo}'.") % { repo: DEFAULT_PUPPET_DEV_URL }
60
+ end
61
+
62
+ # Fetch Updates from remote repository
63
+ fetch_result = PDK::Util::Git.git('-C', puppet_dev_path, 'fetch', 'origin')
64
+
65
+ unless fetch_result[:exit_code].zero?
66
+ PDK.logger.error fetch_result[:stdout]
67
+ PDK.logger.error fetch_result[:stderr]
68
+ raise PDK::CLI::FatalError, _("Unable to fetch from git remote at '%{repo}'.") % { repo: DEFAULT_PUPPET_DEV_URL }
69
+ end
70
+
71
+ # Reset local repo to latest
72
+ reset_result = PDK::Util::Git.git('-C', puppet_dev_path, 'reset', '--hard', 'origin/master')
73
+ return if reset_result[:exit_code].zero?
74
+
75
+ PDK.logger.error reset_result[:stdout]
76
+ PDK.logger.error reset_result[:stderr]
77
+ raise PDK::CLI::FatalError, _("Unable to update git repository at '%{cachedir}'.") % { repo: puppet_dev_path }
78
+ end
79
+
80
+ def find_gem_for(version_str)
81
+ version = parse_specified_version(version_str)
82
+
83
+ # Look for a gem matching exactly the version passed in.
84
+ if version.segments.length == 3
85
+ exact_match_gem = find_gem(Gem::Requirement.create(version))
86
+ return exact_match_gem unless exact_match_gem.nil?
87
+ end
88
+
89
+ # Construct a pessimistic version constraint to find the latest
90
+ # available gem matching the level of specificity of version_str.
91
+ requirement_string = version.approximate_recommendation
92
+ requirement_string += '.0' unless version.segments.length == 1
93
+ latest_requirement = Gem::Requirement.create(requirement_string)
94
+
95
+ latest_available_gem = find_gem(latest_requirement)
96
+
97
+ if latest_available_gem.nil?
98
+ raise ArgumentError, _('Unable to find a Puppet gem matching %{requirement}.') % {
99
+ requirement: latest_requirement,
100
+ }
101
+ end
102
+
103
+ # Only issue this warning if they requested an exact version that isn't available.
104
+ if version.segments.length == 3
105
+ PDK.logger.warn(_('Puppet %{requested_version} is not available, activating %{found_version} instead.') % {
106
+ requested_version: version_str,
107
+ found_version: latest_available_gem[:gem_version].version,
108
+ })
109
+ end
110
+
111
+ latest_available_gem
112
+ end
113
+
114
+ def from_pe_version(version_str)
115
+ version = parse_specified_version(version_str)
116
+
117
+ gem_version = pe_version_map.find do |version_map|
118
+ version_map[:requirement].satisfied_by?(version)
119
+ end
120
+
121
+ if gem_version.nil?
122
+ raise ArgumentError, _('Unable to map Puppet Enterprise version %{pe_version} to a Puppet version.') % {
123
+ pe_version: version_str,
124
+ }
125
+ end
126
+
127
+ PDK.logger.info _('Puppet Enterprise %{pe_version} maps to Puppet %{puppet_version}.') % {
128
+ pe_version: version_str,
129
+ puppet_version: gem_version[:gem_version],
130
+ }
131
+
132
+ find_gem_for(gem_version[:gem_version])
133
+ end
134
+
135
+ def from_module_metadata(metadata = nil)
136
+ if metadata.nil?
137
+ metadata_file = PDK::Util.find_upwards('metadata.json')
138
+
139
+ unless metadata_file
140
+ PDK.logger.warn _('Unable to determine Puppet version for module: no metadata.json present in module.')
141
+ return nil
142
+ end
143
+
144
+ metadata = PDK::Module::Metadata.from_file(metadata_file)
145
+ end
146
+
147
+ metadata.validate_puppet_version_requirement!
148
+ metadata_requirement = metadata.puppet_requirement
149
+
150
+ # Split combined requirements like ">= 4.7.0 < 6.0.0" into their
151
+ # component requirements [">= 4.7.0", "< 6.0.0"]
152
+ pattern = %r{#{Gem::Requirement::PATTERN_RAW}}
153
+ requirement_strings = metadata_requirement['version_requirement'].scan(pattern).map do |req|
154
+ req.compact.join(' ')
155
+ end
156
+
157
+ gem_requirement = Gem::Requirement.create(requirement_strings)
158
+ find_gem(gem_requirement)
159
+ end
160
+
161
+ private
162
+
163
+ def parse_specified_version(version_str)
164
+ Gem::Version.new(version_str)
165
+ rescue ArgumentError
166
+ raise ArgumentError, _('%{version} is not a valid version number.') % {
167
+ version: version_str,
168
+ }
169
+ end
170
+
171
+ def pe_version_map
172
+ @pe_version_map ||= fetch_pe_version_map.map { |version_map|
173
+ maps = version_map['versions'].map do |pe_release|
174
+ requirements = ["= #{pe_release['version']}"]
175
+
176
+ # Some PE release have a .0 Z release, which causes problems when
177
+ # the user specifies "X.Y" expecting to get the latest Z and
178
+ # instead getting the oldest.
179
+ requirements << "!= #{pe_release['version'].gsub(%r{\.\d+\Z}, '')}" if pe_release['version'].end_with?('.0')
180
+ {
181
+ requirement: Gem::Requirement.create(requirements),
182
+ gem_version: pe_release['puppet'],
183
+ }
184
+ end
185
+
186
+ maps << {
187
+ requirement: requirement_from_forge_range(version_map['release']),
188
+ gem_version: version_map['versions'].find { |r| r['version'] == version_map['latest'] }['puppet'],
189
+ }
190
+ }.flatten
191
+ end
192
+
193
+ def fetch_pe_version_map
194
+ map = PDK::Util::VendoredFile.new('pe_versions.json', PE_VERSIONS_URL).read
195
+
196
+ JSON.parse(map)
197
+ rescue PDK::Util::VendoredFile::DownloadError => e
198
+ raise PDK::CLI::FatalError, e.message
199
+ rescue JSON::ParserError
200
+ raise PDK::CLI::FatalError, _('Failed to parse Puppet Enterprise version map file.')
201
+ end
202
+
203
+ def requirement_from_forge_range(range_str)
204
+ Gem::Requirement.create("~> #{range_str.gsub(%r{\.x\Z}, '.0')}")
205
+ end
206
+
207
+ def rubygems_puppet_versions
208
+ return @rubygems_puppet_versions unless @rubygems_puppet_versions.nil?
209
+
210
+ fetcher = Gem::SpecFetcher.fetcher
211
+ puppet_tuples = fetcher.detect(:released) do |spec_tuple|
212
+ spec_tuple.name == 'puppet' && Gem::Platform.match(spec_tuple.platform)
213
+ end
214
+ puppet_versions = puppet_tuples.map { |name, _| name.version }.uniq
215
+ @rubygems_puppet_versions = puppet_versions.sort { |a, b| b <=> a }
216
+ end
217
+
218
+ def find_gem(requirement)
219
+ if PDK::Util.package_install?
220
+ find_in_package_cache(requirement)
221
+ else
222
+ find_in_rubygems(requirement)
223
+ end
224
+ end
225
+
226
+ def find_in_rubygems(requirement)
227
+ version = rubygems_puppet_versions.find { |r| requirement.satisfied_by?(r) }
228
+ version.nil? ? nil : { gem_version: version, ruby_version: PDK::Util::RubyVersion.default_ruby_version }
229
+ end
230
+
231
+ def find_in_package_cache(requirement)
232
+ PDK::Util::RubyVersion.versions.each do |ruby_version, _|
233
+ PDK::Util::RubyVersion.use(ruby_version)
234
+ version = PDK::Util::RubyVersion.available_puppet_versions.find { |r| requirement.satisfied_by?(r) }
235
+ return { gem_version: version, ruby_version: ruby_version } unless version.nil?
236
+ end
237
+
238
+ nil
239
+ end
240
+ end
241
+ end
242
+ end