minimart 0.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 (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