puppetfile-resolver 0.3.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/puppetfile-resolver/data/ruby_ca_certs.pem +732 -851
  4. data/lib/puppetfile-resolver/puppetfile/parser/r10k_eval/module/forge.rb +8 -2
  5. data/lib/puppetfile-resolver/resolution_provider.rb +15 -8
  6. data/lib/puppetfile-resolver/resolution_result.rb +1 -1
  7. data/lib/puppetfile-resolver/resolver.rb +3 -2
  8. data/lib/puppetfile-resolver/spec_searchers/configuration.rb +21 -0
  9. data/lib/puppetfile-resolver/spec_searchers/forge.rb +17 -19
  10. data/lib/puppetfile-resolver/spec_searchers/forge_configuration.rb +17 -0
  11. data/lib/puppetfile-resolver/spec_searchers/git/gclone.rb +82 -0
  12. data/lib/puppetfile-resolver/spec_searchers/git/github.rb +53 -0
  13. data/lib/puppetfile-resolver/spec_searchers/git/gitlab.rb +55 -0
  14. data/lib/puppetfile-resolver/spec_searchers/git.rb +16 -34
  15. data/lib/puppetfile-resolver/spec_searchers/git_configuration.rb +9 -0
  16. data/lib/puppetfile-resolver/spec_searchers/local.rb +3 -2
  17. data/lib/puppetfile-resolver/spec_searchers/local_configuration.rb +13 -0
  18. data/lib/puppetfile-resolver/util.rb +43 -0
  19. data/lib/puppetfile-resolver/version.rb +1 -1
  20. data/puppetfile-cli.rb +14 -1
  21. data/spec/fixtures/modulepath/test_module/README.md +1 -0
  22. data/spec/fixtures/modulepath/test_module/metadata.json +9 -0
  23. data/spec/fixtures/proxy.rb +9 -0
  24. data/spec/integration/deprecation_spec.rb +32 -0
  25. data/spec/integration/kitchensink_spec.rb +49 -0
  26. data/spec/integration/proxy_spec.rb +91 -0
  27. data/spec/spec_helper.rb +2 -0
  28. data/spec/unit/puppetfile-resolver/puppetfile/parser/r10k_eval_spec.rb +19 -4
  29. data/spec/unit/puppetfile-resolver/resolver_spec.rb +44 -1
  30. data/spec/unit/puppetfile-resolver/spec_searchers/git/gclone_spec.rb +50 -0
  31. metadata +29 -8
@@ -14,12 +14,12 @@ module PuppetfileResolver
14
14
 
15
15
  def self.to_document_module(title, args)
16
16
  mod = ::PuppetfileResolver::Puppetfile::ForgeModule.new(title)
17
- mod.version = args if valid_version?(args)
17
+ mod.version = munge_version_string(args) if valid_version?(args)
18
18
  mod
19
19
  end
20
20
 
21
21
  def self.valid_version?(value)
22
- return false unless value.is_a?(String) || value.is_a?(Symbol)
22
+ return false unless value.is_a?(String) || value.is_a?(Symbol) || value.nil?
23
23
  value == :latest || value.nil? || valid_version_string?(value)
24
24
  end
25
25
  private_class_method :valid_version?
@@ -42,6 +42,12 @@ module PuppetfileResolver
42
42
  end
43
43
  end
44
44
  private_class_method :valid_version_string?
45
+
46
+ def self.munge_version_string(value)
47
+ return :latest if value.nil? || value == :latest
48
+ "=#{value}"
49
+ end
50
+ private_class_method :munge_version_string
45
51
  end
46
52
  end
47
53
  end
@@ -5,27 +5,34 @@ require 'puppetfile-resolver/cache/base'
5
5
  require 'puppetfile-resolver/spec_searchers/forge'
6
6
  require 'puppetfile-resolver/spec_searchers/git'
7
7
  require 'puppetfile-resolver/spec_searchers/local'
8
+ require 'puppetfile-resolver/spec_searchers/configuration'
8
9
 
9
10
  module PuppetfileResolver
10
11
  class ResolutionProvider
11
12
  include Molinillo::SpecificationProvider
12
13
 
13
14
  # options
14
- # module_paths : Array of paths
15
- # strict_mode : [Boolean] Whether missing dependencies throw an error (default: false)
15
+ # module_paths : Array of paths (Deprecated)
16
+ # strict_mode : [Boolean] Whether missing dependencies throw an error (default: false)
17
+ # spec_searcher_configuration : PuppetfileResolver::SpecSearchers::Configuration
16
18
  def initialize(puppetfile_document, puppet_version, resolver_ui, options = {})
17
19
  require 'semantic_puppet'
18
20
 
19
21
  @puppetfile_document = puppetfile_document
20
22
  raise 'The UI object must be of type Molinillo::UI' if resolver_ui.nil? || !resolver_ui.is_a?(Molinillo::UI)
21
23
  @resolver_ui = resolver_ui
22
- # TODO: This default crap should move to the resolve class and we just validate (and raise) here
23
- @puppet_module_paths = options[:module_paths].nil? ? [] : options[:module_paths]
24
+ @spec_searcher_configuration = options[:spec_searcher_configuration] || PuppetfileResolver::SpecSearchers::Configuration.new
24
25
  @allow_missing_modules = options[:allow_missing_modules].nil? ? true : options[:allow_missing_modules] == true
25
26
  # There can be only one puppet specification in existance so we pre-load here.
26
27
  @puppet_specification = Models::PuppetSpecification.new(puppet_version)
27
28
  @module_info = {}
28
29
  @cache = options[:cache].nil? ? Cache::Base.new : options[:cache]
30
+
31
+ # Check for deprecated options
32
+ unless options[:module_paths].nil? # rubocop:disable Style/GuardClause
33
+ Warning.warn 'The use of the module_paths option has been deprecated'
34
+ @spec_searcher_configuration.local.puppet_module_paths = options[:module_paths]
35
+ end
29
36
  end
30
37
 
31
38
  # Search for the specifications that match the given dependency.
@@ -137,9 +144,9 @@ module PuppetfileResolver
137
144
  unless mod.nil?
138
145
  case mod.module_type
139
146
  when Puppetfile::FORGE_MODULE
140
- @module_info[dependency.name] = safe_spec_search(dependency) { SpecSearchers::Forge.find_all(dependency, @cache, @resolver_ui) }
147
+ @module_info[dependency.name] = safe_spec_search(dependency) { SpecSearchers::Forge.find_all(dependency, @cache, @resolver_ui, @spec_searcher_configuration.forge) }
141
148
  when Puppetfile::GIT_MODULE
142
- @module_info[dependency.name] = safe_spec_search(dependency) { SpecSearchers::Git.find_all(mod, dependency, @cache, @resolver_ui) }
149
+ @module_info[dependency.name] = safe_spec_search(dependency) { SpecSearchers::Git.find_all(mod, dependency, @cache, @resolver_ui, @spec_searcher_configuration.git) }
143
150
  else # rubocop:disable Style/EmptyElse
144
151
  # Errr.... Nothing
145
152
  end
@@ -147,13 +154,13 @@ module PuppetfileResolver
147
154
  return @module_info[dependency.name] unless @module_info[dependency.name].empty?
148
155
 
149
156
  # It's not in the Puppetfile, so perhaps it's in our modulepath?
150
- @module_info[dependency.name] = safe_spec_search(dependency) { SpecSearchers::Local.find_all(mod, @puppet_module_paths, dependency, @cache, @resolver_ui) }
157
+ @module_info[dependency.name] = safe_spec_search(dependency) { SpecSearchers::Local.find_all(mod, dependency, @cache, @resolver_ui, @spec_searcher_configuration.local) }
151
158
  return @module_info[dependency.name] unless @module_info[dependency.name].empty?
152
159
 
153
160
  # It's not in the Puppetfile and not on disk, so perhaps it's on the Forge?
154
161
  # The forge needs an owner and name to be able to resolve
155
162
  if dependency.name && dependency.owner # rubocop:disable Style/IfUnlessModifier
156
- @module_info[dependency.name] = safe_spec_search(dependency) { SpecSearchers::Forge.find_all(dependency, @cache, @resolver_ui) }
163
+ @module_info[dependency.name] = safe_spec_search(dependency) { SpecSearchers::Forge.find_all(dependency, @cache, @resolver_ui, @spec_searcher_configuration.forge) }
157
164
  end
158
165
 
159
166
  # If we can't find any specifications for the module and we're allowing missing modules
@@ -4,7 +4,7 @@ require 'molinillo'
4
4
 
5
5
  module PuppetfileResolver
6
6
  class ResolutionResult
7
- attr_reader :dependency_graph
7
+ attr_reader :dependency_graph, :puppetfile_document
8
8
 
9
9
  def initialize(dependency_graph, puppetfile_document)
10
10
  raise "Expected Molinillo::DependencyGraph but got #{dependency_graph.class}" unless dependency_graph.is_a?(Molinillo::DependencyGraph)
@@ -20,7 +20,8 @@ module PuppetfileResolver
20
20
 
21
21
  # options
22
22
  # :cache => Cache Object
23
- # :module_paths => Array[String]
23
+ # :module_paths => Array[String] (Deprecated)
24
+ # :spec_searcher_configuration => PuppetfileResolver::SpecSearchers::Configuration
24
25
  def resolve(options = {})
25
26
  if options[:ui]
26
27
  raise 'The UI object must be of type Molinillo::UI' unless options[:ui].is_a?(Molinillo::UI)
@@ -55,7 +56,7 @@ module PuppetfileResolver
55
56
  if mod.version.nil? || mod.version == :latest
56
57
  version = '>= 0' # Note the `>=` is important. Don't use `>`
57
58
  else
58
- version = "=#{mod.version}"
59
+ version = mod.version
59
60
  end
60
61
 
61
62
  result << Models::PuppetfileDependency.new(
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puppetfile-resolver/spec_searchers/forge_configuration'
4
+ require 'puppetfile-resolver/spec_searchers/git_configuration'
5
+ require 'puppetfile-resolver/spec_searchers/local_configuration'
6
+
7
+ module PuppetfileResolver
8
+ module SpecSearchers
9
+ class Configuration
10
+ attr_reader :local
11
+ attr_reader :forge
12
+ attr_reader :git
13
+
14
+ def initialize
15
+ @local = LocalConfiguration.new
16
+ @forge = ForgeConfiguration.new
17
+ @git = GitConfiguration.new
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'puppetfile-resolver/util'
3
4
  require 'puppetfile-resolver/spec_searchers/common'
5
+ require 'puppetfile-resolver/spec_searchers/forge_configuration'
4
6
  require 'uri'
5
7
  require 'net/http'
6
8
  require 'openssl'
@@ -9,9 +11,7 @@ require 'json'
9
11
  module PuppetfileResolver
10
12
  module SpecSearchers
11
13
  module Forge
12
- DEFAULT_FORGE_URI ||= 'https://forgeapi.puppet.com'
13
-
14
- def self.find_all(dependency, cache, resolver_ui)
14
+ def self.find_all(dependency, cache, resolver_ui, config)
15
15
  dep_id = ::PuppetfileResolver::SpecSearchers::Common.dependency_cache_id(self, dependency)
16
16
 
17
17
  # Has the information been cached?
@@ -19,7 +19,7 @@ module PuppetfileResolver
19
19
 
20
20
  result = []
21
21
  # Query the forge
22
- fetch_all_module_releases(dependency.owner, dependency.name, resolver_ui) do |partial_releases|
22
+ fetch_all_module_releases(dependency.owner, dependency.name, resolver_ui, config) do |partial_releases|
23
23
  partial_releases.each do |release|
24
24
  result << Models::ModuleSpecification.new(
25
25
  name: release['module']['name'],
@@ -35,38 +35,36 @@ module PuppetfileResolver
35
35
  result
36
36
  end
37
37
 
38
- def self.fetch_all_module_releases(owner, name, forge_api_url = DEFAULT_FORGE_URI, resolver_ui, &block)
38
+ def self.fetch_all_module_releases(owner, name, resolver_ui, config, &block)
39
39
  raise 'Requires a block to yield' unless block
40
- uri = ::URI.parse("#{forge_api_url}/v3/releases")
40
+ uri = ::URI.parse("#{config.forge_api}/v3/releases")
41
41
  params = { :module => "#{owner}-#{name}", :exclude_fields => 'readme changelog license reference tasks', :limit => 50 }
42
42
  uri.query = ::URI.encode_www_form(params)
43
43
 
44
44
  loops = 0
45
45
  loop do
46
46
  resolver_ui.debug { "Querying the forge for a module with #{uri}" }
47
-
48
- http_options = { :use_ssl => uri.class == URI::HTTPS }
49
- # Because on Windows Ruby doesn't use the Windows certificate store which has up-to date
50
- # CA certs, we can't depend on someone setting the environment variable correctly. So use our
51
- # static CA PEM file if SSL_CERT_FILE is not set.
52
- http_options[:ca_file] = PuppetfileResolver::Util.static_ca_cert_file if ENV['SSL_CERT_FILE'].nil?
53
-
47
+ err_msg = "Unable to find module #{owner}-#{name} on #{config.forge_api}"
48
+ err_msg += config.proxy ? " with proxy #{config.proxy}: " : ': '
54
49
  response = nil
55
- Net::HTTP.start(uri.host, uri.port, http_options) do |http|
56
- request = Net::HTTP::Get.new uri
57
- response = http.request request
50
+
51
+ begin
52
+ response = ::PuppetfileResolver::Util.net_http_get(uri, config.proxy)
53
+ rescue ::StandardError => e
54
+ raise err_msg + e.message
58
55
  end
59
- raise "Expected HTTP Code 200, but received #{response.code} for URI #{uri}: #{response.inspect}" unless response.code == '200'
56
+
57
+ raise err_msg + "Expected HTTP Code 200, but received #{response.code}" unless response.code == '200'
60
58
 
61
59
  reply = ::JSON.parse(response.body)
62
60
  yield reply['results']
63
61
 
64
62
  break if reply['pagination'].nil? || reply['pagination']['next'].nil?
65
- uri = ::URI.parse("#{forge_api_url}#{reply['pagination']['next']}")
63
+ uri = ::URI.parse("#{config.forge_api}#{reply['pagination']['next']}")
66
64
 
67
65
  # Circuit breaker in case the worst happens (max 1000 module releases)
68
66
  loops += 1
69
- raise "Too many Forge API requests #{loops * 50}" if loops > 20
67
+ raise err_msg + "Too many Forge API requests #{loops}" if loops > 20
70
68
  end
71
69
  end
72
70
  private_class_method :fetch_all_module_releases
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppetfileResolver
4
+ module SpecSearchers
5
+ class ForgeConfiguration
6
+ DEFAULT_FORGE_URI ||= 'https://forgeapi.puppet.com'
7
+
8
+ def forge_api
9
+ @forge_api || DEFAULT_FORGE_URI
10
+ end
11
+
12
+ attr_writer :forge_api
13
+
14
+ attr_accessor :proxy
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+ require 'English'
5
+ require 'puppetfile-resolver/util'
6
+ require 'puppetfile-resolver/spec_searchers/common'
7
+ require 'puppetfile-resolver/spec_searchers/git_configuration'
8
+ require 'puppetfile-resolver/util'
9
+ require 'uri'
10
+
11
+ module PuppetfileResolver
12
+ module SpecSearchers
13
+ module Git
14
+ module GClone
15
+ # @summary clones the remote url and reads the metadata file
16
+ # @returns [String] the content of the metadata file
17
+ def self.metadata(puppetfile_module, resolver_ui, config)
18
+ repo_url = puppetfile_module.remote
19
+
20
+ unless PuppetfileResolver::Util.git?
21
+ resolver_ui.debug { 'Git executible not found, unable to use git clone resolution' }
22
+
23
+ return nil
24
+ end
25
+ return nil if repo_url.nil?
26
+ return nil unless valid_http_url?(repo_url)
27
+
28
+ metadata_file = 'metadata.json'
29
+
30
+ ref = puppetfile_module.ref ||
31
+ puppetfile_module.tag ||
32
+ puppetfile_module.commit ||
33
+ puppetfile_module.branch ||
34
+ 'HEAD'
35
+
36
+ resolver_ui.debug { "Querying git repository #{repo_url}" }
37
+
38
+ clone_and_read_file(repo_url, ref, metadata_file, config)
39
+ end
40
+
41
+ # @summary clones the git url and reads the file at the given ref
42
+ # a temp directory will be created and then destroyed during
43
+ # the cloning and reading process
44
+ # @param ref [String] the git ref, branch, commit, tag
45
+ # @param file [String] the file you wish to read
46
+ # @returns [String] the content of the file
47
+ def self.clone_and_read_file(url, ref, file, config)
48
+ clone_cmd = ['git', 'clone', '--bare', '--depth=1', '--single-branch']
49
+ err_msg = ''
50
+ if config.git.proxy
51
+ err_msg += " with proxy #{config.git.proxy}: "
52
+ proxy = "--config \"http.proxy=#{config.git.proxy}\" --config \"https.proxy=#{config.proxy}\""
53
+ clone_cmd.push(proxy)
54
+ end
55
+
56
+ Dir.mktmpdir(nil, config.git.clone_dir) do |dir|
57
+ clone_cmd.push("--branch=#{ref}") if ref != 'HEAD'
58
+ clone_cmd.push(url, dir)
59
+ out, err_out, process = ::PuppetfileResolver::Util.run_command(clone_cmd)
60
+ err_msg += out
61
+ raise err_msg unless process.success?
62
+ Dir.chdir(dir) do
63
+ content, err_out, process = ::PuppetfileResolver::Util.run_command(['git', 'show', "#{ref}:#{file}"])
64
+ raise 'InvalidContent' unless process.success? && content.length > 2
65
+ return content
66
+ end
67
+ end
68
+ end
69
+
70
+ def self.valid_http_url?(url)
71
+ # uri does not work with git urls, return true
72
+ return true if url.start_with?('git@')
73
+
74
+ uri = URI.parse(url)
75
+ uri.is_a?(URI::HTTP) && !uri.host.nil?
76
+ rescue URI::InvalidURIError
77
+ false
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puppetfile-resolver/util'
4
+ require 'puppetfile-resolver/spec_searchers/common'
5
+ require 'puppetfile-resolver/spec_searchers/git_configuration'
6
+
7
+ module PuppetfileResolver
8
+ module SpecSearchers
9
+ module Git
10
+ module GitHub
11
+ def self.metadata(puppetfile_module, resolver_ui, config)
12
+ repo_url = nil
13
+ if puppetfile_module.remote.start_with?('git@github.com:')
14
+ repo_url = puppetfile_module.remote.slice(15..-1)
15
+ repo_url = repo_url.slice(0..-5) if repo_url.end_with?('.git')
16
+ elsif puppetfile_module.remote.start_with?('https://github.com/')
17
+ repo_url = puppetfile_module.remote.slice(19..-1)
18
+ repo_url = repo_url.slice(0..-5) if repo_url.end_with?('.git')
19
+ end
20
+ return nil if repo_url.nil?
21
+
22
+ metadata_url = 'https://raw.githubusercontent.com/' + repo_url + '/'
23
+ if puppetfile_module.ref
24
+ metadata_url += puppetfile_module.ref + '/'
25
+ elsif puppetfile_module.tag
26
+ metadata_url += puppetfile_module.tag + '/'
27
+ else
28
+ # Default to master. Should it raise?
29
+ metadata_url += 'master/'
30
+ end
31
+ metadata_url += 'metadata.json'
32
+
33
+ resolver_ui.debug { "Querying GitHub with #{metadata_url}" }
34
+ err_msg = "Unable to find module at #{puppetfile_module.remote}"
35
+ err_msg += config.proxy ? " with proxy #{config.proxy}: " : ': '
36
+ response = nil
37
+
38
+ begin
39
+ response = ::PuppetfileResolver::Util.net_http_get(metadata_url, config.proxy)
40
+ rescue ::StandardError => e
41
+ raise err_msg + e.message
42
+ end
43
+
44
+ if response.code != '200'
45
+ resolver_ui.debug(err_msg + "Expected HTTP Code 200, but received #{response.code}")
46
+ return ''
47
+ end
48
+ response.body
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puppetfile-resolver/util'
4
+ require 'puppetfile-resolver/spec_searchers/common'
5
+ require 'puppetfile-resolver/spec_searchers/git_configuration'
6
+
7
+ module PuppetfileResolver
8
+ module SpecSearchers
9
+ module Git
10
+ module GitLab
11
+ def self.metadata(puppetfile_module, resolver_ui, config)
12
+ repo_url = nil
13
+ if puppetfile_module.remote.start_with?('git@gitlab.com:')
14
+ repo_url = puppetfile_module.remote.slice(15..-1)
15
+ repo_url = repo_url.slice(0..-5) if repo_url.end_with?('.git')
16
+ elsif puppetfile_module.remote.start_with?('https://gitlab.com/')
17
+ repo_url = puppetfile_module.remote.slice(19..-1)
18
+ repo_url = repo_url.slice(0..-5) if repo_url.end_with?('.git')
19
+ end
20
+ return nil if repo_url.nil?
21
+
22
+ # Example URL
23
+ # https://gitlab.com/simp/pupmod-simp-crypto_policy/-/raw/0.1.4/metadata.json
24
+ metadata_url = 'https://gitlab.com/' + repo_url + '/-/raw/'
25
+ if puppetfile_module.ref
26
+ metadata_url += puppetfile_module.ref + '/'
27
+ elsif puppetfile_module.tag
28
+ metadata_url += puppetfile_module.tag + '/'
29
+ else
30
+ # Default to master. Should it raise?
31
+ metadata_url += 'master/'
32
+ end
33
+ metadata_url += 'metadata.json'
34
+
35
+ resolver_ui.debug { "Querying GitLab with #{metadata_url}" }
36
+ err_msg = "Unable to find module at #{puppetfile_module.remote}"
37
+ err_msg += config.proxy ? " with proxy #{config.proxy}: " : ': '
38
+ response = nil
39
+
40
+ begin
41
+ response = ::PuppetfileResolver::Util.net_http_get(metadata_url, config.proxy)
42
+ rescue ::StandardError => e
43
+ raise err_msg + e.message
44
+ end
45
+
46
+ if response.code != '200'
47
+ resolver_ui.debug(err_msg + "Expected HTTP Code 200, but received #{response.code}")
48
+ return ''
49
+ end
50
+ response.body
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,53 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'puppetfile-resolver/util'
3
4
  require 'puppetfile-resolver/spec_searchers/common'
5
+ require 'puppetfile-resolver/spec_searchers/git_configuration'
6
+ require 'puppetfile-resolver/spec_searchers/git/github'
7
+ require 'puppetfile-resolver/spec_searchers/git/gitlab'
8
+ require 'puppetfile-resolver/spec_searchers/git/gclone'
4
9
 
5
10
  module PuppetfileResolver
6
11
  module SpecSearchers
7
12
  module Git
8
- def self.find_all(puppetfile_module, dependency, cache, resolver_ui)
13
+ def self.find_all(puppetfile_module, dependency, cache, resolver_ui, config)
9
14
  dep_id = ::PuppetfileResolver::SpecSearchers::Common.dependency_cache_id(self, dependency)
10
15
  # Has the information been cached?
11
16
  return cache.load(dep_id) if cache.exist?(dep_id)
12
17
 
13
- # We _could_ git clone this, but it'll take too long. So for now, just
14
- # try and resolve github based repositories by crafting a URL
18
+ # The git clone method takes around (1s) depending on repo size. Not sure if the
19
+ # other methods take longer or shorter but I preserved the legacy code for now.
20
+ # Technically, the gclone class could replace the other classes and speed up
21
+ # this process here.
22
+ metadata = GitHub.metadata(puppetfile_module, resolver_ui, config) ||
23
+ GitLab.metadata(puppetfile_module, resolver_ui, config) ||
24
+ GClone.metadata(puppetfile_module, resolver_ui, config)
15
25
 
16
- repo_url = nil
17
- if puppetfile_module.remote.start_with?('git@github.com:')
18
- repo_url = puppetfile_module.remote.slice(15..-1)
19
- repo_url = repo_url.slice(0..-5) if repo_url.end_with?('.git')
20
- end
21
- if puppetfile_module.remote.start_with?('https://github.com/')
22
- repo_url = puppetfile_module.remote.slice(19..-1)
23
- repo_url = repo_url.slice(0..-5) if repo_url.end_with?('.git')
24
- end
25
-
26
- return [] if repo_url.nil?
27
-
28
- metadata_url = 'https://raw.githubusercontent.com/' + repo_url + '/'
29
- if puppetfile_module.ref
30
- metadata_url += puppetfile_module.ref + '/'
31
- elsif puppetfile_module.tag
32
- metadata_url += puppetfile_module.tag + '/'
33
- else
34
- # Default to master. Should it raise?
35
- metadata_url += 'master/'
36
- end
37
- metadata_url += 'metadata.json'
38
-
39
- require 'net/http'
40
- require 'uri'
41
-
42
- resolver_ui.debug { "Querying the Github with #{metadata_url}" }
43
- response = Net::HTTP.get_response(URI.parse(metadata_url))
44
- if response.code != '200'
26
+ if metadata.nil? || metadata.empty?
27
+ # Cache that we couldn't find the metadata
45
28
  cache.save(dep_id, [])
46
29
  return []
47
30
  end
48
31
 
49
- # TODO: symbolise_object should be in a Util namespace
50
- metadata = ::PuppetfileResolver::Util.symbolise_object(::JSON.parse(response.body))
32
+ metadata = ::PuppetfileResolver::Util.symbolise_object(::JSON.parse(metadata))
51
33
  result = [Models::ModuleSpecification.new(
52
34
  name: metadata[:name],
53
35
  origin: :git,
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppetfileResolver
4
+ module SpecSearchers
5
+ class GitConfiguration
6
+ attr_accessor :proxy, :clone_dir
7
+ end
8
+ end
9
+ end
@@ -1,18 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'puppetfile-resolver/spec_searchers/common'
4
+ require 'puppetfile-resolver/spec_searchers/local_configuration'
4
5
 
5
6
  module PuppetfileResolver
6
7
  module SpecSearchers
7
8
  module Local
8
- def self.find_all(_puppetfile_module, puppet_module_paths, dependency, cache, resolver_ui)
9
+ def self.find_all(_puppetfile_module, dependency, cache, resolver_ui, config)
9
10
  dep_id = ::PuppetfileResolver::SpecSearchers::Common.dependency_cache_id(self, dependency)
10
11
  # Has the information been cached?
11
12
  return cache.load(dep_id) if cache.exist?(dep_id)
12
13
 
13
14
  result = []
14
15
  # Find the module in the modulepaths
15
- puppet_module_paths.each do |module_path|
16
+ config.puppet_module_paths.each do |module_path|
16
17
  next unless Dir.exist?(module_path)
17
18
  module_dir = File.expand_path(File.join(module_path, dependency.name))
18
19
  next unless Dir.exist?(module_dir)
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppetfileResolver
4
+ module SpecSearchers
5
+ class LocalConfiguration
6
+ attr_accessor :puppet_module_paths
7
+
8
+ def initialize
9
+ @puppet_module_paths = []
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'open3'
4
+
3
5
  module PuppetfileResolver
4
6
  module Util
5
7
  def self.symbolise_object(object)
@@ -19,5 +21,46 @@ module PuppetfileResolver
19
21
  def self.static_ca_cert_file
20
22
  @static_ca_cert_file ||= File.expand_path(File.join(__dir__, 'data', 'ruby_ca_certs.pem'))
21
23
  end
24
+
25
+ # Execute a HTTP/S GET query and return the response
26
+ # @param [String, URI] uri The URI to request
27
+ # @param [nil, String, URI] proxy The URI of the proxy server to use. Defaults to nil (No proxy server)
28
+ # @return [Net::HTTPResponse] the response of the request
29
+ def self.net_http_get(uri, proxy = nil)
30
+ uri = URI.parse(uri) unless uri.is_a?(URI)
31
+
32
+ http_options = { :use_ssl => uri.class == URI::HTTPS }
33
+ # Because on Windows Ruby doesn't use the Windows certificate store which has up-to date
34
+ # CA certs, we can't depend on someone setting the environment variable correctly. So use our
35
+ # static CA PEM file if SSL_CERT_FILE is not set.
36
+ http_options[:ca_file] = PuppetfileResolver::Util.static_ca_cert_file if ENV['SSL_CERT_FILE'].nil?
37
+
38
+ start_args = [uri.host, uri.port]
39
+
40
+ unless proxy.nil?
41
+ proxy = URI.parse(proxy) unless proxy.is_a?(URI)
42
+ start_args.concat([proxy.host, proxy.port, proxy.user, proxy.password])
43
+ end
44
+
45
+ Net::HTTP.start(*start_args, http_options) { |http| return http.request(Net::HTTP::Get.new(uri)) }
46
+ nil
47
+ end
48
+
49
+ # @summary runs the command on the shell
50
+ # @param cmd [Array] an array of command and args
51
+ # @returns [Array] the result of running the comand and the process
52
+ # @example run_command(['git', '--version'])
53
+ def self.run_command(cmd)
54
+ Open3.capture3(*cmd)
55
+ end
56
+
57
+ # @summary checks if git is installed and on the path
58
+ # @returns [Boolean] true if git is found in the path
59
+ def self.git?
60
+ Open3.capture3('git', '--version')
61
+ true
62
+ rescue Errno::ENOENT
63
+ false
64
+ end
22
65
  end
23
66
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PuppetfileResolver
4
- VERSION ||= '0.3.0'
4
+ VERSION ||= '0.6.0'
5
5
  end
data/puppetfile-cli.rb CHANGED
@@ -18,6 +18,7 @@ class CommandLineParser
18
18
  cache_dir: nil,
19
19
  module_paths: [],
20
20
  path: nil,
21
+ proxy: nil,
21
22
  puppet_version: nil,
22
23
  strict: false
23
24
  }
@@ -41,6 +42,10 @@ class CommandLineParser
41
42
  args[:debug] = true
42
43
  end
43
44
 
45
+ opts.on('--proxy=PROXY_URL', 'HTTP/S Proxy server to use. For example http://localhost:8888') do |proxy|
46
+ args[:proxy] = proxy
47
+ end
48
+
44
49
  opts.on('-s', '--strict', 'Do not allow missing dependencies. Default false which marks dependencies as missing and does not raise an error.') do
45
50
  args[:strict] = true
46
51
  end
@@ -88,7 +93,15 @@ if options[:debug]
88
93
  else
89
94
  ui = nil
90
95
  end
91
- opts = { cache: cache, ui: ui, module_paths: options[:module_paths], allow_missing_modules: !options[:strict] }
96
+
97
+ config = PuppetfileResolver::SpecSearchers::Configuration.new
98
+ config.local.puppet_module_paths = options[:module_paths]
99
+ unless options[:proxy].nil?
100
+ config.git.proxy = options[:proxy]
101
+ config.forge.proxy = options[:proxy]
102
+ end
103
+
104
+ opts = { cache: cache, ui: ui, spec_searcher_configuration: config, allow_missing_modules: !options[:strict] }
92
105
 
93
106
  # Resolve
94
107
  result = resolver.resolve(opts)
@@ -0,0 +1 @@
1
+ This test fixture contains a valid module with classes, types and functions.