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,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