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
+ require 'minimart/utils/http'
2
+
3
+ module Minimart
4
+ module Mirror
5
+
6
+ # Source represents a single remote source to fetch cookbooks from.
7
+ # Each source is specified in the inventory using the 'sources' key.
8
+ class Source
9
+
10
+ # @return [String] The URL for this source
11
+ attr_accessor :base_url
12
+
13
+ # @param [String] base_url The URL for this source
14
+ def initialize(base_url)
15
+ @base_url = base_url
16
+ end
17
+
18
+ # Search through the cookbooks available at this source, and return
19
+ # the relevant version if it is available.
20
+ # @return [Minimart::Mirror::SourceCookbook]
21
+ def find_cookbook(cookbook_name, version)
22
+ cookbooks.find do |cookbook|
23
+ cookbook.name == cookbook_name && cookbook.version == version
24
+ end
25
+ end
26
+
27
+ # Query this source for it's available cookbooks using the '/universe' endpoint.
28
+ # @return [Array<Minimart::Mirror::SourceCookbook]
29
+ def cookbooks
30
+ @cookbooks ||= fetch_universe_data.each_with_object([]) do |cookbook_info, memo|
31
+ name, versions = cookbook_info
32
+ memo.concat versions.map { |version, attrs| build_cookbook(name, version, attrs) }
33
+ end
34
+ end
35
+
36
+ def to_s
37
+ base_url
38
+ end
39
+
40
+ private
41
+
42
+ def build_cookbook(name, version, attrs)
43
+ attrs = attrs.merge(name: name, version: version)
44
+ SourceCookbook.new(attrs)
45
+ end
46
+
47
+ def fetch_universe_data
48
+ Configuration.output.puts "Fetching the universe for #{base_url} ..."
49
+
50
+ Utils::Http.get_json(base_url, 'universe')
51
+
52
+ rescue RestClient::ResourceNotFound
53
+ raise Error::UniverseNotFoundError.new "no universe found for #{base_url}"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,77 @@
1
+ require 'minimart/download/cookbook'
2
+
3
+ module Minimart
4
+ module Mirror
5
+
6
+ # A wrapper around a cookbook as found in the universe.json file from an
7
+ # external source (Chef Supermarket, etc...).
8
+ class SourceCookbook
9
+
10
+ # @return [String] the name of the cookbook
11
+ attr_accessor :name
12
+
13
+ # @return [String] the version of the cookbook
14
+ attr_accessor :version
15
+
16
+ # @return [Hash<String,String>] any dependencies the cookbook has
17
+ attr_accessor :dependencies
18
+
19
+ # @return [String] the path to the cookbook
20
+ attr_accessor :location_path
21
+
22
+ # @return [String] the type of location the cookbook is stored in (supermarket, etc.)
23
+ attr_accessor :location_type
24
+
25
+ # @return [String] URL to download the cookbook
26
+ attr_accessor :download_url
27
+
28
+ # @param [Hash] opts
29
+ # @option opts [String] name The name of the cookbook
30
+ # @option opts [String] version The version of the cookbook
31
+ # @option opts [String] location_path The path to the cookbook
32
+ # @option opts [String] download_url URL to download the cookbook
33
+ # @option opts [Hash] dependencies A hash containing any of the cookbook's dependencies.
34
+ # @option opts [String] location_type The type of location the cookbook is stored in (supermarket, etc.)
35
+ def initialize(opts)
36
+ @name = fetch_from_options(opts, 'name')
37
+ @version = fetch_from_options(opts, 'version')
38
+ @location_path = fetch_from_options(opts, 'location_path')
39
+ @download_url = fetch_from_options(opts, 'download_url')
40
+ @dependencies = fetch_from_options(opts, 'dependencies') || {}
41
+ @location_type = fetch_from_options(opts, 'location_type')
42
+ end
43
+
44
+ # Download this remote cookbook
45
+ # @yield [Dir] The path to the downloaded cookbook. This directory will be removed when the block exits.
46
+ def fetch(&block)
47
+ Download::Cookbook.new(self).fetch(&block)
48
+ end
49
+
50
+ # Convert this remote cookbook to a Hash
51
+ # @return [Hash]
52
+ def to_hash
53
+ {
54
+ source_type: location_type,
55
+ location: location_path
56
+ }
57
+ end
58
+
59
+ # Get the location_path as a URI
60
+ # @return [URI]
61
+ def location_path_uri
62
+ URI.parse(location_path)
63
+ end
64
+
65
+ def web_friendly_version
66
+ version.gsub('.', '_')
67
+ end
68
+
69
+ private
70
+
71
+ def fetch_from_options(opts, key)
72
+ opts[key] || opts[key.to_sym]
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,37 @@
1
+ module Minimart
2
+ module Mirror
3
+ # A collection of Minimart::Mirror::Source
4
+ class Sources < Array
5
+
6
+ # @param [Array<String>] source_urls An array of source URL's specified in the inventory
7
+ def initialize(source_urls = [])
8
+ source_urls.each { |source_url| add_source(source_url) }
9
+ end
10
+
11
+ # Iterate over each cookbook defined in each source
12
+ # @yield [Minimart::Mirror::SourceCookbook]
13
+ def each_cookbook(&block)
14
+ each { |source| source.cookbooks.each(&block) }
15
+ end
16
+
17
+ # Find the first cookbook from the avaiable sources with a matching name, and
18
+ # version
19
+ # @param [String] name The name of the cookbook to search for
20
+ # @param [String] version The version of the cookbook to search for
21
+ def find_cookbook(name, version)
22
+ each do |source|
23
+ cookbook = source.find_cookbook(name, version)
24
+ return cookbook if cookbook
25
+ end
26
+
27
+ raise Error::CookbookNotFound, "The cookbook #{name} with the version #{version} could not be found"
28
+ end
29
+
30
+ private
31
+
32
+ def add_source(source_url)
33
+ self << Source.new(source_url)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ module Minimart
2
+ # Wrapper for IO to provide colored output.
3
+ class Output
4
+
5
+ attr_reader :io
6
+
7
+ def initialize(io)
8
+ @io = io
9
+ end
10
+
11
+ def puts(*args)
12
+ io.puts(args)
13
+ end
14
+
15
+ def puts_red(str)
16
+ puts_color(31, str)
17
+ end
18
+
19
+ def puts_green(str)
20
+ puts_color(32, str)
21
+ end
22
+
23
+ def puts_yellow(str)
24
+ puts_color(33, str)
25
+ end
26
+
27
+ private
28
+
29
+ def puts_color(color_code, str)
30
+ self.puts "\e[#{color_code}m#{str}\e[0m"
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ require 'archive/tar/minitar'
2
+ require 'zlib'
3
+ require 'minimart/mirror/download_metadata'
4
+
5
+ module Minimart
6
+ module Utils
7
+
8
+ # Archive manages tarring, and gzipping files
9
+ module Archive
10
+
11
+ # Extract a tar.gz archive
12
+ # @param [String] archive_file The path to the archive file
13
+ # @param [String] destination The directory to unpack the archive to
14
+ def self.extract_archive(archive_file, destination)
15
+ tar = Zlib::GzipReader.new(File.open(archive_file, 'rb'))
16
+ ::Archive::Tar::Minitar.unpack(tar, destination)
17
+ end
18
+
19
+ # Build a tar.tz archive from a directory
20
+ # @param [Minimart::Cookbook] cookbook The cookbook to archive
21
+ # @param [String] destination The path to store the tar.gz archive
22
+ def self.pack_archive(cookbook, destination)
23
+ Dir.mktmpdir do |tmp|
24
+ Dir.chdir(tmp) do
25
+ archive_dir = File.join(tmp, cookbook.name)
26
+ FileUtils.mkdir_p(archive_dir)
27
+ FileUtils.cp_r(File.join(cookbook.path, '.'), archive_dir)
28
+ meta_file = File.join(archive_dir, Minimart::Mirror::DownloadMetadata::FILE_NAME)
29
+ FileUtils.remove_entry(meta_file) if File.exists?(meta_file)
30
+
31
+ tgz = Zlib::GzipWriter.new(File.open(destination, 'wb'))
32
+ ::Archive::Tar::Minitar.pack(cookbook.name, tgz)
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,34 @@
1
+ module Minimart
2
+ module Utils
3
+ # FileHelper contains helper methods for dealing with the file system.
4
+ module FileHelper
5
+
6
+ # Find the first cookbook in the given path
7
+ # @param [String] path The directory to search for cookbooks in
8
+ # @return [String] The path to the cookbook
9
+ def self.cookbook_path_in_directory(path)
10
+ cookbook_in_path?(path) ? path : find_cookbooks_in_directory(path).first
11
+ end
12
+
13
+ # List all of the cookbooks in a given directory
14
+ # @param [String] path The directory to find cookbooks in
15
+ # @return [Array<String>] An array of paths to any cookbooks found in the supplied path.
16
+ def self.find_cookbooks_in_directory(path)
17
+ Dir.glob(File.join(path, '/*/')).select { |d| cookbook_in_path?(d) }
18
+ end
19
+
20
+ # Determine whether or not a given directory contains a cookbook.
21
+ # @param [String] path The directory to check
22
+ # @return [Boolean]
23
+ def self.cookbook_in_path?(path)
24
+ file_exists?(File.join(path, 'metadata.json')) ||
25
+ file_exists?(File.join(path, 'metadata.rb'))
26
+ end
27
+
28
+ def self.file_exists?(path)
29
+ File.exists?(path)
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,60 @@
1
+ require 'rest_client'
2
+ require 'uri'
3
+
4
+ module Minimart
5
+ module Utils
6
+ # A collection of methods to help issue HTTP requests
7
+ module Http
8
+
9
+ # Issue a GET request to a URL, and return parsed JSON.
10
+ # @param [String] url The base URL to hit
11
+ # @param [String] path The path to the RESTful resource to fetch
12
+ # @return [Hash] The parsed JSON response
13
+ def self.get_json(url, path=nil)
14
+ JSON.parse(get(url, path, accept: 'application/json'))
15
+ end
16
+
17
+ # Issue a GET request to a URL
18
+ # @param [String] base_url The base URL to hit
19
+ # @param [String] path The path to the RESTful resource to fetch
20
+ def self.get(base_url, path=nil, headers={})
21
+ headers = headers.merge(verify_ssl: Minimart::Configuration.verify_ssl)
22
+
23
+ resource = RestClient::Resource.new(base_url.to_s)
24
+ path ? resource[path].get(headers) : resource.get(headers)
25
+ end
26
+
27
+ # GET a binary file
28
+ # @param [String] base_name A base name to give the returned file
29
+ # @param [String] url The base URL to hit
30
+ # @param [String] path The path to the RESTful resource to fetch
31
+ # @return [Tempfile]
32
+ def self.get_binary(base_name, url, path=nil)
33
+ result = Tempfile.new(base_name)
34
+ result.binmode
35
+ result.write(get(url, path))
36
+ result.close(false)
37
+ result
38
+ end
39
+
40
+ # Build a URL from a base URL, and a path
41
+ # @param [String] base_url The base URL to hit
42
+ # @param [String] path
43
+ def self.build_url(base_url, path=nil)
44
+ result = (base_url =~ /\A[a-z].*:\/\//i) ? base_url : "http://#{base_url}"
45
+ result = URI.parse(result).to_s
46
+ result = concat_url_fragment(result, path)
47
+ return result
48
+ end
49
+
50
+ def self.concat_url_fragment(frag_one, frag_two)
51
+ return frag_one unless frag_two
52
+ result = frag_one
53
+ result << '/' unless result[-1] == '/'
54
+ result << ((frag_two[0] == '/') ? frag_two[1..-1] : frag_two)
55
+ result
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ module Minimart
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,10 @@
1
+ module Minimart
2
+ # The Web namespace is used for building the cookbook index, and generating
3
+ # any HTML.
4
+ module Web
5
+ require 'minimart/web/cookbooks'
6
+ require 'minimart/web/html_generator'
7
+ require 'minimart/web/template_helper'
8
+ require 'minimart/web/universe_generator'
9
+ end
10
+ end
@@ -0,0 +1,78 @@
1
+ module Minimart
2
+ module Web
3
+ # Generate "show" pages for a set of cookbooks
4
+ class CookbookShowPageGenerator
5
+ include TemplateHelper
6
+
7
+ # @return [String] the directory to put any generated HTML in
8
+ attr_reader :web_directory
9
+
10
+ # @return [Minimart::Web::Cookbooks] the cookbooks to generate show pages for
11
+ attr_reader :cookbooks
12
+
13
+ # @param [Hash] opts
14
+ # @option opts [String] :web_directory The directory to put any generated HTML in
15
+ # @option opts [String] :cookbooks The cookbooks to generate show pages for
16
+ def initialize(opts = {})
17
+ @web_directory = opts[:web_directory]
18
+ @cookbooks = opts[:cookbooks]
19
+ end
20
+
21
+ # Generate the HTML!
22
+ def generate
23
+ clean_web_cookbooks_directory
24
+ make_web_cookbooks_directory
25
+ create_html_files
26
+ end
27
+
28
+ private
29
+
30
+ def clean_web_cookbooks_directory
31
+ return unless Dir.exists?(cookbooks_directory)
32
+ FileUtils.remove_entry(cookbooks_directory)
33
+ end
34
+
35
+ def make_web_cookbooks_directory
36
+ FileUtils.mkdir_p(cookbooks_directory)
37
+ end
38
+
39
+ def create_html_files
40
+ cookbooks.each do |cookbook_name, versions|
41
+ versions.each do |cookbook|
42
+ write_to_file(file(cookbook), template_content(cookbook, versions))
43
+ end
44
+ end
45
+ end
46
+
47
+ def file(cookbook)
48
+ FileUtils.mkdir_p(File.join(web_directory, cookbook_dir(cookbook)))
49
+ File.join(web_directory, cookbook_file(cookbook))
50
+ end
51
+
52
+ def template_content(cookbook, versions)
53
+ render_in_base_layout do
54
+ render_template('cookbook_show.erb', self, {cookbook: cookbook, other_versions: versions})
55
+ end
56
+ end
57
+
58
+ def write_to_file(file_path, content)
59
+ File.open(file_path, 'w+') { |f| f.write(content) }
60
+ end
61
+
62
+ def cookbooks_directory
63
+ File.join(web_directory, 'cookbooks')
64
+ end
65
+
66
+ def cookbook_for_requirement(name, version_requirement)
67
+ (cookbooks[name] || []).find do |c|
68
+ c.satisfies_requirement?(version_requirement)
69
+ end
70
+ end
71
+
72
+ def level
73
+ 2
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,83 @@
1
+ require 'forwardable'
2
+
3
+ require 'minimart/cookbook'
4
+ require 'minimart/utils/file_helper'
5
+
6
+ module Minimart
7
+ module Web
8
+ # Given a path to the inventory directory, this class will generate the necessary
9
+ # JSON output to power the main dashboard, and return cookbooks in a format
10
+ # that can be used to build the various web pages.
11
+ class Cookbooks
12
+ extend Forwardable
13
+
14
+ include Enumerable
15
+
16
+ FILE_NAME = 'data.json'
17
+
18
+ # @return [String] The path to the cookbook inventory
19
+ attr_reader :inventory_directory
20
+
21
+ # @param [Hash] opts
22
+ # @option opts [String] :inventory_directory The path to the cookbook inventory
23
+ def initialize(opts)
24
+ @inventory_directory = opts[:inventory_directory]
25
+ @data_structure = {}
26
+
27
+ generate
28
+ end
29
+
30
+ # Get a JSON representation of the most recent version of each cookbook
31
+ # found in the inventory_directory.
32
+ # @return [Hash]
33
+ def to_json
34
+ map do |cookbook_name, cookbook_versions|
35
+ cookbook_versions.first.to_hash.merge(available_versions: cookbook_versions.size)
36
+ end.to_json
37
+ end
38
+
39
+ def_delegators :data_structure, :each, :keys, :values, :[]
40
+
41
+ # Get a non-nested version of the cookbooks structure
42
+ def individual_cookbooks
43
+ values.flatten
44
+ end
45
+
46
+ # Add a cookbook to the data structure.
47
+ # @param [Minimart::Cookbook] cookbook The cookbook to add
48
+ def add(cookbook)
49
+ data_structure[cookbook.name] ||= []
50
+ data_structure[cookbook.name] << cookbook
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :data_structure
56
+
57
+ def generate
58
+ build_data_structure
59
+ sort_data
60
+ end
61
+
62
+ def build_data_structure
63
+ cookbooks.each { |cookbook| add(cookbook) }
64
+ end
65
+
66
+ # Sort cookbooks in version desc order
67
+ def sort_data
68
+ data_structure.values.map! do |versions|
69
+ versions.sort!.reverse!
70
+ end
71
+ end
72
+
73
+ def cookbooks
74
+ inventory_cookbook_paths.map { |path| Minimart::Cookbook.from_path(path) }
75
+ end
76
+
77
+ def inventory_cookbook_paths
78
+ Utils::FileHelper.find_cookbooks_in_directory(inventory_directory)
79
+ end
80
+
81
+ end
82
+ end
83
+ end