pdk-akerl 1.8.0.1

Sign up to get free protection for your applications and to get access to all the features.
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