minimart 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +21 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +13 -0
  5. data/Gemfile +3 -0
  6. data/README.md +198 -0
  7. data/Rakefile +45 -0
  8. data/bin/minimart +4 -0
  9. data/lib/minimart.rb +15 -0
  10. data/lib/minimart/cli.rb +93 -0
  11. data/lib/minimart/commands/mirror.rb +30 -0
  12. data/lib/minimart/commands/web.rb +91 -0
  13. data/lib/minimart/configuration.rb +46 -0
  14. data/lib/minimart/cookbook.rb +173 -0
  15. data/lib/minimart/download/cookbook.rb +70 -0
  16. data/lib/minimart/download/git_cache.rb +60 -0
  17. data/lib/minimart/download/git_repository.rb +41 -0
  18. data/lib/minimart/error.rb +32 -0
  19. data/lib/minimart/inventory_requirement/base_requirement.rb +81 -0
  20. data/lib/minimart/inventory_requirement/git_requirement.rb +87 -0
  21. data/lib/minimart/inventory_requirement/git_requirements_builder.rb +101 -0
  22. data/lib/minimart/inventory_requirement/local_path_requirement.rb +45 -0
  23. data/lib/minimart/inventory_requirement/local_requirements_builder.rb +31 -0
  24. data/lib/minimart/inventory_requirement/supermarket_requirements_builder.rb +35 -0
  25. data/lib/minimart/mirror.rb +12 -0
  26. data/lib/minimart/mirror/dependency_graph.rb +73 -0
  27. data/lib/minimart/mirror/download_metadata.rb +57 -0
  28. data/lib/minimart/mirror/inventory_builder.rb +143 -0
  29. data/lib/minimart/mirror/inventory_configuration.rb +74 -0
  30. data/lib/minimart/mirror/inventory_requirements.rb +104 -0
  31. data/lib/minimart/mirror/local_store.rb +107 -0
  32. data/lib/minimart/mirror/source.rb +57 -0
  33. data/lib/minimart/mirror/source_cookbook.rb +77 -0
  34. data/lib/minimart/mirror/sources.rb +37 -0
  35. data/lib/minimart/output.rb +34 -0
  36. data/lib/minimart/utils/archive.rb +39 -0
  37. data/lib/minimart/utils/file_helper.rb +34 -0
  38. data/lib/minimart/utils/http.rb +60 -0
  39. data/lib/minimart/version.rb +3 -0
  40. data/lib/minimart/web.rb +10 -0
  41. data/lib/minimart/web/cookbook_show_page_generator.rb +78 -0
  42. data/lib/minimart/web/cookbooks.rb +83 -0
  43. data/lib/minimart/web/dashboard_generator.rb +46 -0
  44. data/lib/minimart/web/html_generator.rb +52 -0
  45. data/lib/minimart/web/markdown_parser.rb +47 -0
  46. data/lib/minimart/web/template_helper.rb +132 -0
  47. data/lib/minimart/web/universe_generator.rb +109 -0
  48. data/minimart.gemspec +36 -0
  49. data/spec/fixtures/sample_cookbook.tar.gz +0 -0
  50. data/spec/fixtures/sample_cookbook/Berksfile +3 -0
  51. data/spec/fixtures/sample_cookbook/CHANGELOG.md +3 -0
  52. data/spec/fixtures/sample_cookbook/Gemfile +16 -0
  53. data/spec/fixtures/sample_cookbook/LICENSE +3 -0
  54. data/spec/fixtures/sample_cookbook/README.md +42 -0
  55. data/spec/fixtures/sample_cookbook/Thorfile +5 -0
  56. data/spec/fixtures/sample_cookbook/Vagrantfile +90 -0
  57. data/spec/fixtures/sample_cookbook/chefignore +94 -0
  58. data/spec/fixtures/sample_cookbook/metadata.rb +9 -0
  59. data/spec/fixtures/sample_cookbook/recipes/default.rb +8 -0
  60. data/spec/fixtures/sample_inventory.yml +16 -0
  61. data/spec/fixtures/simple_git_inventory.yml +8 -0
  62. data/spec/fixtures/simple_inventory.yml +6 -0
  63. data/spec/fixtures/simple_local_path_inventory.yml +5 -0
  64. data/spec/fixtures/universe.json +42 -0
  65. data/spec/fixtures/vcr_cassettes/local_path_cookbooks.yml +3316 -0
  66. data/spec/fixtures/vcr_cassettes/location_specific_cookbooks.yml +3316 -0
  67. data/spec/fixtures/vcr_cassettes/supermarket_cookbooks_graph.yml +905 -0
  68. data/spec/fixtures/vcr_cassettes/supermarket_cookbooks_installing_cookbooks.yml +4218 -0
  69. data/spec/lib/minimart/cli_spec.rb +104 -0
  70. data/spec/lib/minimart/commands/mirror_spec.rb +37 -0
  71. data/spec/lib/minimart/commands/web_spec.rb +75 -0
  72. data/spec/lib/minimart/configuration_spec.rb +54 -0
  73. data/spec/lib/minimart/cookbook_spec.rb +137 -0
  74. data/spec/lib/minimart/download/cookbook_spec.rb +135 -0
  75. data/spec/lib/minimart/download/git_cache_spec.rb +69 -0
  76. data/spec/lib/minimart/download/git_repository_spec.rb +39 -0
  77. data/spec/lib/minimart/error_spec.rb +18 -0
  78. data/spec/lib/minimart/inventory_requirement/base_requirement_spec.rb +38 -0
  79. data/spec/lib/minimart/inventory_requirement/git_requirement_spec.rb +92 -0
  80. data/spec/lib/minimart/inventory_requirement/git_requirements_builder_spec.rb +130 -0
  81. data/spec/lib/minimart/inventory_requirement/local_path_requirement_spec.rb +35 -0
  82. data/spec/lib/minimart/inventory_requirement/local_requirements_builder_spec.rb +33 -0
  83. data/spec/lib/minimart/inventory_requirement/supermarket_requirements_builder_spec.rb +69 -0
  84. data/spec/lib/minimart/mirror/dependency_graph_spec.rb +123 -0
  85. data/spec/lib/minimart/mirror/download_metadata_spec.rb +73 -0
  86. data/spec/lib/minimart/mirror/inventory_builder_spec.rb +195 -0
  87. data/spec/lib/minimart/mirror/inventory_configuration_spec.rb +86 -0
  88. data/spec/lib/minimart/mirror/inventory_requirements_spec.rb +78 -0
  89. data/spec/lib/minimart/mirror/local_store_spec.rb +64 -0
  90. data/spec/lib/minimart/mirror/source_spec.rb +54 -0
  91. data/spec/lib/minimart/mirror/sources_spec.rb +50 -0
  92. data/spec/lib/minimart/output_spec.rb +29 -0
  93. data/spec/lib/minimart/utils/archive_spec.rb +38 -0
  94. data/spec/lib/minimart/utils/file_helper_spec.rb +43 -0
  95. data/spec/lib/minimart/utils/http_spec.rb +37 -0
  96. data/spec/lib/minimart/web/cookbook_show_page_generator_spec.rb +101 -0
  97. data/spec/lib/minimart/web/cookbooks_spec.rb +70 -0
  98. data/spec/lib/minimart/web/dashboard_generator_spec.rb +33 -0
  99. data/spec/lib/minimart/web/html_generator_spec.rb +34 -0
  100. data/spec/lib/minimart/web/markdown_parser_spec.rb +54 -0
  101. data/spec/lib/minimart/web/template_helper_spec.rb +86 -0
  102. data/spec/lib/minimart/web/universe_generator_spec.rb +125 -0
  103. data/spec/spec_helper.rb +35 -0
  104. data/spec/support/file_system.rb +22 -0
  105. data/web/_assets/javascripts/app.js +164 -0
  106. data/web/_assets/javascripts/backbone.min.js +6 -0
  107. data/web/_assets/javascripts/jquery.min.js +4 -0
  108. data/web/_assets/javascripts/jquery.tabslet.min.js +9 -0
  109. data/web/_assets/javascripts/manifest.js +5 -0
  110. data/web/_assets/javascripts/underscore.min.js +5 -0
  111. data/web/_assets/stylesheets/font-awesome.min.css +4 -0
  112. data/web/_assets/stylesheets/font-mfizz.css +318 -0
  113. data/web/_assets/stylesheets/main.css +720 -0
  114. data/web/_assets/stylesheets/manifest.css +4 -0
  115. data/web/_assets/stylesheets/normalize.css +427 -0
  116. data/web/assets/fonts/FontAwesome.otf +0 -0
  117. data/web/assets/fonts/font-mfizz.eot +0 -0
  118. data/web/assets/fonts/font-mfizz.svg +1344 -0
  119. data/web/assets/fonts/font-mfizz.ttf +0 -0
  120. data/web/assets/fonts/font-mfizz.woff +0 -0
  121. data/web/assets/fonts/fontawesome-webfont.eot +0 -0
  122. data/web/assets/fonts/fontawesome-webfont.svg +520 -0
  123. data/web/assets/fonts/fontawesome-webfont.ttf +0 -0
  124. data/web/assets/fonts/fontawesome-webfont.woff +0 -0
  125. data/web/assets/images/header-slim.jpg +0 -0
  126. data/web/assets/images/icon-search.png +0 -0
  127. data/web/assets/images/mad-glory-logo.png +0 -0
  128. data/web/assets/images/main-gradient.png +0 -0
  129. data/web/assets/images/top-bar-logo.png +0 -0
  130. data/web/assets/javascripts/application.min.js +5 -0
  131. data/web/assets/stylesheets/application.min.css +4 -0
  132. data/web/templates/cookbook_show.erb +96 -0
  133. data/web/templates/dashboard.erb +81 -0
  134. data/web/templates/layout.erb +38 -0
  135. metadata +433 -0
@@ -0,0 +1,32 @@
1
+ module Minimart
2
+ # The collection of Minimart specific errors.
3
+ module Error
4
+ class BaseError < StandardError; end
5
+
6
+ # Raised when a dependency cannot be met.
7
+ class UnresolvedDependency < BaseError; end
8
+
9
+ # Raised when there is an error parsing the inventory file.
10
+ class InvalidInventoryError < BaseError; end
11
+
12
+ # Raised when a source does not respond to '/universe' correctly.
13
+ class UniverseNotFoundError < BaseError; end
14
+
15
+ # Raised when none of the available sources have a given cookbook.
16
+ class CookbookNotFound < BaseError; end
17
+
18
+ # Raised when there is a conflict between different versions of the same cookbook.
19
+ class BrokenDependency < BaseError; end
20
+
21
+ # Raised when Minimart encounters a cookbook with a location type that it can't handle
22
+ class UnknownLocationType < BaseError; end
23
+
24
+ # Gracefully handle any errors raised by Minimart, and exit with a failure
25
+ # status code.
26
+ # @param [Minimart::Error::BaseError] ex
27
+ def self.handle_exception(ex)
28
+ Configuration.output.puts_red(ex.message)
29
+ exit false
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,81 @@
1
+ module Minimart
2
+ module InventoryRequirement
3
+
4
+ # BaseRequirement represents a single cookbook entry as listed in the inventory.
5
+ # BaseRequirement is a generic interface for other inventory requirements.
6
+ class BaseRequirement
7
+
8
+ # @return [String] The name of the cookbook
9
+ attr_reader :name
10
+
11
+ # @return [String] The SemVer requirement for the cookbook
12
+ attr_reader :version_requirement
13
+
14
+ # @return [Minimart::Cookbook] The resolved cookbook
15
+ attr_reader :cookbook
16
+
17
+ # @param [String] name The name of the cookbook
18
+ # @param [Hash] opts
19
+ # @option opts [String] :version_requirement The SemVer requirement for the cookbook
20
+ def initialize(name, opts)
21
+ @name = name
22
+ @version_requirement = opts[:version_requirement]
23
+ end
24
+
25
+ # Determine whether or not this is a requirement which explicitly defines
26
+ # it's location (e.g. Git repo). Defaults to FALSE.
27
+ # @return [Boolean]
28
+ def explicit_location?
29
+ false
30
+ end
31
+
32
+ # The requirements to download this cookbook.
33
+ # @return [Hash]
34
+ def requirements
35
+ # if this cookbook has it's location specified, we instead return it's
36
+ # dependencies as we don't need to resolve them elsewhere
37
+ explicit_location? ? cookbook.dependencies : {name => version_requirement}
38
+ end
39
+
40
+ # Download a cookbook that has it's location explicitly defined (see #explicit_location?)
41
+ # @yield [Minimart::Cookbook]
42
+ def fetch_cookbook(&block)
43
+ return unless explicit_location?
44
+
45
+ download_cookbook do |cookbook|
46
+ @cookbook = cookbook
47
+ block.call(cookbook) if block
48
+ end
49
+ end
50
+
51
+ # Determine whether or not a version requirement was defined for the given
52
+ # cookbook.
53
+ # @return [Boolean]
54
+ def version_requirement?
55
+ !!version_requirement
56
+ end
57
+
58
+ # Convert the requirement to a Hash.
59
+ # @return [Hash]
60
+ def to_hash
61
+ {}
62
+ end
63
+
64
+ # Determine if a cookbook in the inventory has metadata matching this requirement
65
+ # @param [Minimart::Mirror::DownloadMetadata] metadata The download metadata for a cookbook
66
+ # in the inventory.
67
+ # @return [Boolean] Defaults to true
68
+ def matching_source?(metadata)
69
+ return true
70
+ end
71
+
72
+ private
73
+
74
+ # This method must be overridden by any subclasses.
75
+ def download_cookbook(&block)
76
+ nil
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,87 @@
1
+ require 'minimart/download/git_repository'
2
+
3
+ module Minimart
4
+ module InventoryRequirement
5
+
6
+ # A single Git requirement for a cookbook as specified in the inventory.
7
+ # This represents a single brach, ref, or tag for a cookbook.
8
+ class GitRequirement < BaseRequirement
9
+
10
+ # @return [String] the location to fetch this cookbook from.
11
+ attr_reader :location
12
+
13
+ # @return [String] the branch to checkout once this cookbook has been fetched.
14
+ attr_reader :branch
15
+
16
+ # @return [String] the SHA of the Git commit to checkout once this cookbook has been fetched.
17
+ attr_reader :ref
18
+
19
+ # @return [String] the tag to checkout once this cookbook has been fetched
20
+ attr_reader :tag
21
+
22
+ # @param [String] name The name of the cookbook defined by this requirement.
23
+ # @param [Hash] opts
24
+ # @option opts [String] branch The branch to checkout once this cookbook has been fetched.
25
+ # @option opts [String] tag The tag to checkout once this cookbook has been fetched
26
+ # @option opts [String] ref The SHA of the Git commit to checkout once this cookbook has been fetched.
27
+ def initialize(name, opts)
28
+ super
29
+ @branch = opts[:branch]
30
+ @ref = opts[:ref]
31
+ @tag = opts[:tag]
32
+ @location = opts[:location]
33
+ end
34
+
35
+ # Git requirements explicitly define their location, so this method will return true.
36
+ # @return [Boolean] TRUE
37
+ def explicit_location?
38
+ true
39
+ end
40
+
41
+ # Convert this requirement to a hash
42
+ # @return [Hash]
43
+ def to_hash
44
+ result = super
45
+ result[:source_type] = :git
46
+ result[:location] = location
47
+ result[:commitish_type] = commitish_type
48
+ result[:commitish] = commitish
49
+ result
50
+ end
51
+
52
+ # Determine if a Git cookbook in the inventory has metadata matching this requirement.
53
+ # This method will return true if the metadata has the same commit information
54
+ # as this requirement.
55
+ # @param [Minimart::Mirror::DownloadMetadata] metadata The download metadata for a cookbook
56
+ # in the inventory.
57
+ # @return [Boolean] Defaults to true
58
+ def matching_source?(metadata)
59
+ metadata['source_type'] == 'git' &&
60
+ metadata['commitish_type'] == commitish_type.to_s &&
61
+ (metadata['commitish_type'] == 'ref' ? true : metadata['commitish'] == commitish.to_s)
62
+ end
63
+
64
+ private
65
+
66
+ def download_cookbook(&block)
67
+ Configuration.output.puts "-- Fetching '#{name}[#{commitish}]' from '#{location}'"
68
+
69
+ downloader = Download::GitRepository.new(location)
70
+ downloader.fetch(commitish) do |path_to_cookbook|
71
+ block.call(Minimart::Cookbook.from_path(path_to_cookbook))
72
+ end
73
+ end
74
+
75
+ def commitish
76
+ ref || branch || tag
77
+ end
78
+
79
+ def commitish_type
80
+ return :ref if ref
81
+ return :branch if branch
82
+ return :tag if tag
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,101 @@
1
+ require 'minimart/inventory_requirement/git_requirement'
2
+
3
+ module Minimart
4
+ module InventoryRequirement
5
+ # This class is used to parse any Git requirements specified in the inventory
6
+ # and build Minimart::Inventory::GitRequirements from them.
7
+ class GitRequirementsBuilder
8
+
9
+ # @return [String] the name of the cookbook defined by this requirement.
10
+ attr_reader :name
11
+
12
+ # @return [String] the location to fetch this cookbook from.
13
+ attr_reader :location
14
+
15
+ # @return [Array<String>] a listing of branches to checkout when fetching this cookbook.
16
+ attr_reader :branches
17
+
18
+ # @return [Array<String>] a listing of tags to checkout when fetching this cookbook.
19
+ attr_reader :tags
20
+
21
+ # @return [Array<String>] a listing of refs to checkout when fetching this cookbook.
22
+ attr_reader :refs
23
+
24
+ # @param [String] name The name of the cookbook defined by this requirement.
25
+ # @param [Hash] reqs
26
+ # * 'git' [Hash] The git specific requirements for this cookbook
27
+ # * 'branches' [Array<String>] A listing of branches to checkout when fetching this cookbook.
28
+ # * 'branch' [String] A single branch to checkout when fetching this cookbook.
29
+ # * 'tags' [Array<String>] A listing of tags to checkout when fetching this cookbook.
30
+ # * 'tag' [String] A single tag to checkout when fetching this cookbook.
31
+ # * 'refs' [Array<String>] A listing of ref to checkout when fetching this cookbook.
32
+ # * 'ref' [String] A single ref to checkout when fetching this cookbook.
33
+ def initialize(name, reqs)
34
+ @name = name
35
+ git_reqs = reqs.fetch('git', {})
36
+
37
+ @location = git_reqs['location']
38
+ @branches = raw_location_type_requirement(%w[branches branch], git_reqs)
39
+ @tags = raw_location_type_requirement(%w[tags tag], git_reqs)
40
+ @refs = raw_location_type_requirement(%w[refs ref], git_reqs)
41
+
42
+ validate_requirements(git_reqs)
43
+ end
44
+
45
+ # Build the git requirements.
46
+ # @return [Array<Minimart::InventoryRequirement::GitRequirement>]
47
+ def build
48
+ from_branches + from_tags + from_refs
49
+ end
50
+
51
+ private
52
+
53
+ def from_branches
54
+ branches.map { |b| build_requirement(:branch, b) }
55
+ end
56
+
57
+ def from_tags
58
+ tags.map { |t| build_requirement(:tag, t) }
59
+ end
60
+
61
+ def from_refs
62
+ refs.map { |r| build_requirement(:ref, r) }
63
+ end
64
+
65
+ def build_requirement(type, value)
66
+ InventoryRequirement::GitRequirement.new(name, {type => value}.merge(location: location))
67
+ end
68
+
69
+ def raw_location_type_requirement(location_types, requirements)
70
+ location_types.inject([]) do |memo, type|
71
+ if requirements[type]
72
+ req = requirements[type]
73
+ req = [req] if req.is_a?(String)
74
+ memo.concat(req)
75
+ end
76
+ memo
77
+ end
78
+ end
79
+
80
+ def validate_requirements(reqs)
81
+ return if reqs.nil? || reqs.empty?
82
+
83
+ validate_location
84
+ validate_commitish
85
+ end
86
+
87
+ def validate_location
88
+ return unless location.nil? || location.empty?
89
+ raise Minimart::Error::InvalidInventoryError,
90
+ "'#{name}' specifies Git requirements, but does not have a location."
91
+ end
92
+
93
+ def validate_commitish
94
+ return unless branches.empty? && tags.empty? && refs.empty?
95
+ raise Minimart::Error::InvalidInventoryError,
96
+ "'#{name}' specified Git requirements, but does not provide a branch|tag|ref"
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,45 @@
1
+ module Minimart
2
+ module InventoryRequirement
3
+
4
+ # A requirement to a cookbook found in the local file system
5
+ class LocalPathRequirement < BaseRequirement
6
+
7
+ # @return [String] the path to the cookbook
8
+ attr_reader :path
9
+
10
+ # @param [String] name The name of the cookbook
11
+ # @param [Hash] opts
12
+ # @option opts [String] path The path to the cookbook
13
+ def initialize(name, opts)
14
+ super
15
+ @path = opts[:path]
16
+ end
17
+
18
+ # Local path requirements explicitly define their location, so this method will return true.
19
+ # @return [Boolean] TRUE
20
+ def explicit_location?
21
+ true
22
+ end
23
+
24
+ # Convert this requirement to a hash
25
+ # @return [Hash]
26
+ def to_hash
27
+ result = super
28
+ result[:source_type] = :local_path
29
+ result
30
+ end
31
+
32
+ def matching_source?(metadata)
33
+ metadata['source_type'] == 'local_path'
34
+ end
35
+
36
+ private
37
+
38
+ def download_cookbook(&block)
39
+ Configuration.output.puts "-- Fetching '#{name}' from path '#{path}'"
40
+ block.call(Minimart::Cookbook.from_path(path))
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ require 'minimart/inventory_requirement/local_path_requirement'
2
+
3
+ module Minimart
4
+ module InventoryRequirement
5
+ # This class is used to parse any local path requirements specified in the inventory
6
+ # and build Minimart::Inventory::LocalPathRequirements from them.
7
+ class LocalRequirementsBuilder
8
+
9
+ # @return [String] the name of the cookbook defined by this requirement.
10
+ attr_reader :name
11
+
12
+ # @return [String] the path to the cookbook
13
+ attr_reader :path
14
+
15
+ # @param [String] name The name of the cookbook defined by this requirement.
16
+ # @param [Hash] reqs
17
+ # @option reqs [String] 'path' The path to the cookbook
18
+ def initialize(name, reqs)
19
+ @name = name
20
+ @path = reqs['path']
21
+ end
22
+
23
+ # Build the local path requirements.
24
+ # @return [Array<Minimart::InventoryRequirement::LocalPathRequirement>]
25
+ def build
26
+ return [] if path.nil? || path.empty?
27
+ [InventoryRequirement::LocalPathRequirement.new(name, path: path)]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ require 'minimart/inventory_requirement/base_requirement'
2
+
3
+ module Minimart
4
+ module InventoryRequirement
5
+
6
+ # This class is used to parse any Supermarket requirements specified in the inventory
7
+ # and build Minimart::Inventory::BaseRequirements from them.
8
+ class SupermarketRequirementsBuilder
9
+
10
+ # @return [String] the name of the cookbook
11
+ attr_reader :name
12
+
13
+ # @return [Array<String>] an array of versions to fetch for this cookbook
14
+ attr_reader :versions
15
+
16
+ # @param [String] name The name of the cookbook
17
+ # @param [Hash] reqs
18
+ # * 'versions' [Array<String>] A listing of versions to fetch.
19
+ # * 'version' [String] A single version to fetch.
20
+ def initialize(name, reqs)
21
+ @name = name
22
+ @versions = reqs['versions'] || reqs['version'] || []
23
+ @versions = [@versions] if @versions.is_a? String
24
+ end
25
+
26
+ # Build the Supemarket requirements.
27
+ # @return [Array<Minimart::InventoryRequirement::BaseRequirement>]
28
+ def build
29
+ versions.map do |v|
30
+ InventoryRequirement::BaseRequirement.new(name, version_requirement: v)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,12 @@
1
+ module Minimart
2
+ # The Mirror namespace is used to build the local cookbook inventory.
3
+ module Mirror
4
+ require 'minimart/mirror/dependency_graph'
5
+ require 'minimart/mirror/inventory_builder'
6
+ require 'minimart/mirror/inventory_configuration'
7
+ require 'minimart/mirror/local_store'
8
+ require 'minimart/mirror/source_cookbook'
9
+ require 'minimart/mirror/source'
10
+ require 'minimart/mirror/sources'
11
+ end
12
+ end
@@ -0,0 +1,73 @@
1
+ require 'solve'
2
+
3
+ module Minimart
4
+ module Mirror
5
+
6
+ # A dependency graph for managing any remote cookbooks and their dependencies.
7
+ # This can be used to resolve any requirements found in the inventory file.
8
+ class DependencyGraph
9
+
10
+
11
+ attr_reader :graph
12
+ attr_reader :inventory_requirements
13
+
14
+ def initialize
15
+ @graph = Solve::Graph.new
16
+ @inventory_requirements = []
17
+ end
18
+
19
+ # Add an artifact (cookbook), and its dependencies to the graph.
20
+ # @param [Minimart::Mirror::SourceCookbook] cookbook
21
+ def add_artifact(cookbook)
22
+ return if source_cookbook_added?(cookbook.name, cookbook.version)
23
+
24
+ graph.artifact(cookbook.name, cookbook.version).tap do |artifact|
25
+ cookbook.dependencies.each do |dependency|
26
+ name, requirements = dependency
27
+ artifact.depends(name, requirements)
28
+ end
29
+ end
30
+ end
31
+
32
+ # Determine whether or not the graph has a given cookbook.
33
+ # @param [String] name The name of the cookbook
34
+ # @param [String] version The version of the cookbook
35
+ # @return [Boolean]
36
+ def source_cookbook_added?(name, version)
37
+ graph.artifact?(name, version)
38
+ end
39
+
40
+ # Get a cookbook out of the graph.
41
+ # @param [Minimart::Mirror::SourceCookbook] cookbook The cookbook to fetch.
42
+ # @return [Solve::Artifact]
43
+ def find_graph_artifact(cookbook)
44
+ graph.find(cookbook.name, cookbook.version)
45
+ end
46
+
47
+ # Add a new requirement to be resolved by the graph
48
+ # @param [Hash] requirements (ex. { 'minimart' => '> 0.0.1' })
49
+ def add_requirement(requirements = {})
50
+ inventory_requirements.concat(requirements.to_a)
51
+ end
52
+
53
+ # Resolve any requirements against the graph
54
+ # @return [Array] The requirements as resolved by the graph (ex. [['minimart', '0.0.5']])
55
+ # @raise [Minimart::Error::UnresolvedDependency] Raised when a dependency cannot be resolved
56
+ def resolved_requirements
57
+ inventory_requirements.inject([]) do |result, requirement|
58
+ result.concat(resolve_requirement(requirement).to_a)
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def resolve_requirement(requirement)
65
+ Solve.it!(graph, [requirement])
66
+
67
+ rescue Solve::Errors::NoSolutionError => e
68
+ raise Error::UnresolvedDependency, e.message
69
+ end
70
+
71
+ end
72
+ end
73
+ end