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,57 @@
1
+ module Minimart
2
+ module Mirror
3
+ # This class can be used to parse, and create `.minimart.json` files to
4
+ # store information about when, and how Minimart downloaded a given cookbook.
5
+ class DownloadMetadata
6
+
7
+ FILE_NAME = '.minimart.json'
8
+
9
+ # @return [String] the path to the directory containing the cookbook.
10
+ attr_reader :path_to_cookbook
11
+
12
+ # @return [Hash] the contents of the metadata file.
13
+ attr_reader :metadata
14
+
15
+ # @param [String] path_to_cookbook The path to the directory containing the cookbook.
16
+ def initialize(path_to_cookbook)
17
+ @path_to_cookbook = path_to_cookbook
18
+ parse_file
19
+ end
20
+
21
+ # Write the given contents to the metadata file. This will overwrite any
22
+ # existing contents.
23
+ # @param [Hash] contents The hash of data to write to the file.
24
+ def write(contents = {})
25
+ File.open(file_path, 'w+') do |file|
26
+ @metadata = contents
27
+ @metadata.merge!('downloaded_at' => Time.now.utc.iso8601)
28
+
29
+ file.write(metadata.to_json)
30
+ end
31
+ end
32
+
33
+ # @return [Time] The downloaded_at time found in the metadata file.
34
+ def downloaded_at
35
+ return unless self['downloaded_at']
36
+ Time.iso8601(metadata['downloaded_at']).utc
37
+ end
38
+
39
+ def [](key)
40
+ metadata[key] if metadata
41
+ end
42
+
43
+ private
44
+
45
+ def parse_file
46
+ return unless File.exists?(file_path)
47
+ file_contents = File.open(file_path).read
48
+ @metadata = JSON.parse(file_contents)
49
+ end
50
+
51
+ def file_path
52
+ File.join(path_to_cookbook, FILE_NAME)
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,143 @@
1
+ module Minimart
2
+ module Mirror
3
+
4
+ # InventoryBuilder coordinates downloading any cookbooks, and their dependencies.
5
+ class InventoryBuilder
6
+
7
+ # @return [Minimart::Mirror::InventoryConfiguration] The user specified inventory config.
8
+ attr_reader :inventory_configuration
9
+
10
+ # @return [Minimart::Mirror::DependencyGraph]
11
+ attr_reader :graph
12
+
13
+ # @return [Minimart::Mirror::LocalStore] the local store manages the inventory directory contents
14
+ attr_reader :local_store
15
+
16
+ # @param [String] inventory_directory The directory to store the inventory.
17
+ # @param [Minimart::Mirror::InventoryConfiguration] inventory_configuration The inventory as defined by a user of Minimart.
18
+ def initialize(inventory_directory, inventory_configuration)
19
+ @graph = DependencyGraph.new
20
+ @local_store = LocalStore.new(inventory_directory)
21
+ @inventory_configuration = inventory_configuration
22
+ end
23
+
24
+ # Build the inventory!
25
+ def build!
26
+ install_cookbooks_with_explicit_location
27
+ add_source_cookbooks_to_graph
28
+ add_requirements_to_graph
29
+ fetch_inventory
30
+ display_success_message
31
+
32
+ ensure
33
+ clear_cache
34
+ end
35
+
36
+ private
37
+
38
+ # First we must install any cookbooks with a location specification (git, local path, etc..).
39
+ # These cookbooks and their associated metadata (any dependencies they have) take
40
+ # precedence over information found elsewhere.
41
+ def install_cookbooks_with_explicit_location
42
+ inventory_requirements.each_with_explicit_location do |requirement|
43
+ requirement.fetch_cookbook do |cookbook|
44
+ validate_cookbook_against_local_store(cookbook, requirement)
45
+ add_artifact_to_graph(cookbook)
46
+ add_cookbook_to_local_store(cookbook.path, requirement.to_hash)
47
+ end
48
+ end
49
+ end
50
+
51
+ # Fetch the universe from any of the defined sources, and add them as artifacts
52
+ # to the dependency resolution graph.
53
+ def add_source_cookbooks_to_graph
54
+ sources.each_cookbook { |cookbook| add_artifact_to_graph(cookbook) }
55
+ end
56
+
57
+ # Add any cookbooks defined in the inventory file as a requirement to the graph
58
+ def add_requirements_to_graph
59
+ inventory_requirements.each do |requirement|
60
+ graph.add_requirement(requirement.requirements)
61
+ end
62
+ end
63
+
64
+ def fetch_inventory
65
+ resolved_requirements.each do |resolved_requirement|
66
+ install_cookbook(*resolved_requirement)
67
+ end
68
+ end
69
+
70
+ def resolved_requirements
71
+ graph.resolved_requirements
72
+ end
73
+
74
+ def install_cookbook(name, version)
75
+ if cookbook_already_installed?(name, version)
76
+ Configuration.output.puts_yellow("cookbook already installed: #{name}-#{version}.")
77
+ return
78
+ end
79
+
80
+ verify_dependency_can_be_installed(name, version)
81
+
82
+ source_cookbook = cookbook_from_source(name, version)
83
+ source_cookbook.fetch do |cookbook|
84
+ add_cookbook_to_local_store(cookbook.path, source_cookbook.to_hash)
85
+ end
86
+ end
87
+
88
+ def cookbook_already_installed?(name, version)
89
+ local_store.installed?(name, version)
90
+ end
91
+
92
+ def verify_dependency_can_be_installed(name, version)
93
+ return unless non_required_version?(name, version)
94
+
95
+ msg = "The dependency #{name}-#{version} could not be installed."
96
+ msg << " This is because a cookbook listed in the inventory depends on a version of '#{name}'"
97
+ msg << " that does not match the explicit requirements for the '#{name}' cookbook."
98
+ raise Error::BrokenDependency, msg
99
+ end
100
+
101
+ def non_required_version?(name, version)
102
+ !inventory_requirements.version_required?(name, version)
103
+ end
104
+
105
+ # We need to validate that if this cookbook is in the store
106
+ # it came from the same source, otherwise we could
107
+ # possibly override a cookbook
108
+ def validate_cookbook_against_local_store(cookbook, requirement)
109
+ local_store.validate_resolved_requirement(cookbook, requirement)
110
+ end
111
+
112
+ def add_cookbook_to_local_store(cookbook_path, data = {})
113
+ local_store.add_cookbook_from_path(cookbook_path, data)
114
+ end
115
+
116
+ def cookbook_from_source(name, version)
117
+ sources.find_cookbook(name, version)
118
+ end
119
+
120
+ def add_artifact_to_graph(cookbook)
121
+ graph.add_artifact(cookbook)
122
+ end
123
+
124
+ def inventory_requirements
125
+ inventory_configuration.requirements
126
+ end
127
+
128
+ def sources
129
+ inventory_configuration.sources
130
+ end
131
+
132
+ def display_success_message
133
+ Configuration.output.puts_green %{Minimart is done building your inventory!}
134
+ Configuration.output.puts_green %{The inventory can be found in #{local_store.directory_path}}
135
+ end
136
+
137
+ def clear_cache
138
+ Minimart::Download::GitCache.instance.clear
139
+ end
140
+
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,74 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+
4
+ require 'minimart/mirror/inventory_requirements'
5
+ require 'minimart/utils/file_helper'
6
+
7
+ module Minimart
8
+ module Mirror
9
+
10
+ # This class is responsible for parsing a user defined Minimart configuration file.
11
+ class InventoryConfiguration
12
+
13
+ # The path to the inventory configuration file
14
+ attr_reader :inventory_config_path
15
+
16
+ # @param [String] inventory_config_path The path to the inventory configuration file
17
+ def initialize(inventory_config_path)
18
+ @inventory_config_path = inventory_config_path
19
+ @configuration = parse_config_file
20
+ parse_global_configuration
21
+ end
22
+
23
+ # The collection of files defined in the inventory file
24
+ # @return [Minimart::Mirror::Sources]
25
+ def sources
26
+ @sources ||= Sources.new(raw_sources)
27
+ end
28
+
29
+ # The collection of cookbook requirements defined in the inventory file
30
+ # @return [Array]
31
+ def requirements
32
+ @cookbooks ||= InventoryRequirements.new(raw_cookbooks)
33
+ end
34
+
35
+ private
36
+
37
+ # The raw parsed configuration file
38
+ attr_reader :configuration
39
+
40
+ def parse_config_file
41
+ unless Utils::FileHelper.file_exists?(inventory_config_path)
42
+ raise Error::InvalidInventoryError, 'The inventory configuration file could not be found'
43
+ end
44
+
45
+ file = File.open(inventory_config_path).read
46
+ erb = ERB.new(file).result(binding)
47
+ YAML.load(erb)
48
+ end
49
+
50
+ def parse_global_configuration
51
+ return unless (conf = configuration['configuration']) && conf.is_a?(Hash)
52
+
53
+ Minimart::Configuration.tap do |c|
54
+ c.chef_server_config = conf.fetch('chef', {})
55
+ c.github_config = conf.fetch('github', [])
56
+ c.verify_ssl = conf['verify_ssl']
57
+ end
58
+ end
59
+
60
+ def raw_sources
61
+ configuration['sources'] || []
62
+ end
63
+
64
+ def raw_cookbooks
65
+ configuration['cookbooks'].tap do |cookbooks|
66
+ if cookbooks.nil? || cookbooks.empty?
67
+ raise Error::InvalidInventoryError, 'Minimart could not find any cookbooks defined in the inventory'
68
+ end
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,104 @@
1
+ require 'minimart/inventory_requirement/supermarket_requirements_builder'
2
+ require 'minimart/inventory_requirement/git_requirements_builder'
3
+ require 'minimart/inventory_requirement/local_requirements_builder'
4
+
5
+ module Minimart
6
+ module Mirror
7
+
8
+ # The collection of requirements as defined in the inventory file.
9
+ class InventoryRequirements
10
+ include Enumerable
11
+
12
+ # @return [Hash<String, Hash>] The cookbooks listed in the inventory file
13
+ attr_reader :raw_cookbooks
14
+
15
+ # @param [Hash<String, Hash>] raw_cookbooks The cookbooks listed in the inventory file
16
+ def initialize(raw_cookbooks)
17
+ @raw_cookbooks = raw_cookbooks
18
+ @requirements = {}
19
+
20
+ parse_cookbooks
21
+ end
22
+
23
+ # Iterate over the requirements
24
+ # @yield [Minimart::Inventory::BaseRequirement]
25
+ def each(&block)
26
+ requirements.values.flatten.each &block
27
+ end
28
+
29
+ def each_with_explicit_location(&block)
30
+ each do |req|
31
+ block.call(req) if req.explicit_location?
32
+ end
33
+ end
34
+
35
+ # This method will determine whether or not a version of a cookbook
36
+ # resolves a constraint defined in the inventory file. This method
37
+ # can be used to verify that we aren't downloading a non specified version
38
+ # of a cookbook (e.g. possibly downloading cookbooks that a user wants,
39
+ # but in versions that they do not)
40
+ # @param [String] name The name of the cookbook to verify
41
+ # @param [String] version The cookbook version to check
42
+ # @return [Boolean] Return true if this version solves a requirement
43
+ # as specified in the inventory. This method will also return true for
44
+ # any cookbooks not specified in the inventory.
45
+ def version_required?(name, version)
46
+ (!has_cookbook?(name)) ||
47
+ solves_explicit_requirement?(name, version)
48
+ end
49
+
50
+ private
51
+
52
+ attr_reader :requirements
53
+
54
+ def has_cookbook?(name)
55
+ requirements.has_key?(name)
56
+ end
57
+
58
+ def solves_explicit_requirement?(name, version)
59
+ requirements[name].each do |req|
60
+ next unless req.version_requirement?
61
+ return true if Semverse::Constraint.new(req.version_requirement).satisfies?(version)
62
+ end
63
+ return false
64
+ end
65
+
66
+ # Build Minimart::Inventory requirements from the inventory.
67
+ def parse_cookbooks
68
+ raw_cookbooks.each do |name, reqs|
69
+ @requirements[name] = build_requirements_for(name, (reqs || {}))
70
+ end
71
+ end
72
+
73
+ def build_requirements_for(name, reqs)
74
+ get_requirements_for(name, reqs).tap do |parsed_requirements|
75
+ validate_requirements(name, parsed_requirements)
76
+ end
77
+ end
78
+
79
+ def get_requirements_for(name, reqs)
80
+ (market_requirements(name, reqs) +
81
+ git_requirements(name, reqs) +
82
+ local_path_requirements(name, reqs)).flatten.compact
83
+ end
84
+
85
+ def validate_requirements(name, reqs)
86
+ return unless reqs.nil? || reqs.empty?
87
+ raise Minimart::Error::InvalidInventoryError,
88
+ "Minimart could not find any requirements for '#{name}'"
89
+ end
90
+
91
+ def market_requirements(name, reqs)
92
+ InventoryRequirement::SupermarketRequirementsBuilder.new(name, reqs).build
93
+ end
94
+
95
+ def git_requirements(name, reqs)
96
+ InventoryRequirement::GitRequirementsBuilder.new(name, reqs).build
97
+ end
98
+
99
+ def local_path_requirements(name, reqs)
100
+ InventoryRequirement::LocalRequirementsBuilder.new(name, reqs).build
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,107 @@
1
+ require 'minimart/mirror/download_metadata'
2
+ require 'minimart/utils/file_helper'
3
+
4
+ module Minimart
5
+ module Mirror
6
+
7
+ # The LocalStore manages what is stored in the local inventory, and adding
8
+ # cookbook directories to the local inventory. The LocalStore will automatically
9
+ # load anything that has been stored in the inventory upon initialization.
10
+ class LocalStore
11
+
12
+ # @return [String] the path to the local inventory
13
+ attr_reader :directory_path
14
+
15
+ # @param [String] directory_path The path to the local inventory
16
+ def initialize(directory_path)
17
+ @directory_path = directory_path
18
+ @cookbooks = {}
19
+
20
+ load_local_inventory
21
+ end
22
+
23
+ # :nodoc:
24
+ def add_cookbook_to_store(name, version)
25
+ cookbooks[name] ||= []
26
+ cookbooks[name] << version
27
+ end
28
+
29
+ # Copy a given cookbook to the local store, and record any metadata
30
+ # about how the cookbook was downloaded.
31
+ # @param [String] path The path to the cookbook to add to the store
32
+ # @param [Hash] download_data Any data to record about the cookbook in a Minimart metadata file.
33
+ def add_cookbook_from_path(path, download_data = {})
34
+ cookbook_from_path(path).tap do |cookbook|
35
+ add_cookbook_to_store(cookbook.name, cookbook.version)
36
+ copy_cookbook(cookbook.path, local_path_for(cookbook))
37
+ write_download_data(cookbook, download_data)
38
+ end
39
+ end
40
+
41
+ # Determine whether or not a cookbook has been added to the LocalStore
42
+ # @return [Boolean]
43
+ def installed?(cookbook_name, cookbook_version)
44
+ !!(cookbooks[cookbook_name] &&
45
+ cookbooks[cookbook_name].include?(cookbook_version))
46
+ end
47
+
48
+ # Validate that a new resolved requirement is not in the local store
49
+ # with different requirements. If we download two different branches
50
+ # of the same cookbook and they both resolve to the same version, then we
51
+ # raise an exception.
52
+ # @param [Minimart::Cookbook] new_cookbook
53
+ # @param [Minimart::InventoryRequirement::BaseRequirement] requirement
54
+ # @raise [Minimart::Error::BrokenDependency]
55
+ def validate_resolved_requirement(new_cookbook, requirement)
56
+ return unless installed?(new_cookbook.name, new_cookbook.version)
57
+
58
+ existing_cookbook = load_cookbook(new_cookbook)
59
+ unless requirement.matching_source?(existing_cookbook.download_metadata)
60
+ raise Minimart::Error::BrokenDependency, "A version of #{new_cookbook} already exists in the inventory from a different source."
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ attr_reader :cookbooks
67
+
68
+ def copy_cookbook(source, destination)
69
+ FileUtils.rm_rf(destination) if Dir.exists?(destination)
70
+ FileUtils.cp_r(source, destination)
71
+
72
+ # clean destination directory
73
+ git_dir = File.join(destination, '/.git')
74
+ FileUtils.rm_rf(git_dir) if Dir.exists?(git_dir)
75
+ end
76
+
77
+ def load_local_inventory
78
+ Utils::FileHelper.find_cookbooks_in_directory(directory).each do |path|
79
+ cookbook = cookbook_from_path(path)
80
+ add_cookbook_to_store(cookbook.name, cookbook.version)
81
+ end
82
+ end
83
+
84
+ def load_cookbook(cookbook)
85
+ cookbook_from_path(local_path_for(cookbook))
86
+ end
87
+
88
+ def cookbook_from_path(path)
89
+ Minimart::Cookbook.from_path(Utils::FileHelper.cookbook_path_in_directory(path))
90
+ end
91
+
92
+ def directory
93
+ # lazily create the directory to hold the store
94
+ @directory ||= FileUtils.mkdir_p(directory_path).first
95
+ end
96
+
97
+ def local_path_for(cookbook)
98
+ File.join(directory, "/#{cookbook}")
99
+ end
100
+
101
+ def write_download_data(cookbook, download_data = {})
102
+ Minimart::Mirror::DownloadMetadata.new(local_path_for(cookbook)).write(download_data)
103
+ end
104
+
105
+ end
106
+ end
107
+ end