knife_cookbook_dependencies 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. data/.gitignore +22 -0
  2. data/.rbenv-version +1 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +90 -0
  5. data/LICENSE +22 -0
  6. data/README.rdoc +5 -0
  7. data/Rakefile +29 -0
  8. data/knife_cookbook_dependencies.gemspec +30 -0
  9. data/lib/chef/knife/cookbook_dependencies_install.rb +17 -0
  10. data/lib/knife_cookbook_dependencies/cookbook.rb +184 -0
  11. data/lib/knife_cookbook_dependencies/cookbookfile.rb +33 -0
  12. data/lib/knife_cookbook_dependencies/dependency_reader.rb +46 -0
  13. data/lib/knife_cookbook_dependencies/dsl.rb +7 -0
  14. data/lib/knife_cookbook_dependencies/git.rb +86 -0
  15. data/lib/knife_cookbook_dependencies/knife_utils.rb +13 -0
  16. data/lib/knife_cookbook_dependencies/lockfile.rb +25 -0
  17. data/lib/knife_cookbook_dependencies/metacookbook.rb +18 -0
  18. data/lib/knife_cookbook_dependencies/shelf.rb +70 -0
  19. data/lib/knife_cookbook_dependencies/version.rb +3 -0
  20. data/lib/knife_cookbook_dependencies.rb +42 -0
  21. data/spec/fixtures/cookbooks/example_cookbook/README.md +12 -0
  22. data/spec/fixtures/cookbooks/example_cookbook/metadata.rb +6 -0
  23. data/spec/fixtures/cookbooks/example_cookbook/recipes/default.rb +8 -0
  24. data/spec/lib/knife_cookbook_dependencies/cookbook_spec.rb +109 -0
  25. data/spec/lib/knife_cookbook_dependencies/cookbookfile_spec.rb +26 -0
  26. data/spec/lib/knife_cookbook_dependencies/dependency_reader_spec.rb +42 -0
  27. data/spec/lib/knife_cookbook_dependencies/dsl_spec.rb +29 -0
  28. data/spec/lib/knife_cookbook_dependencies/git_spec.rb +58 -0
  29. data/spec/lib/knife_cookbook_dependencies/shelf_spec.rb +37 -0
  30. data/spec/spec_helper.rb +21 -0
  31. data/todo.txt +13 -0
  32. metadata +163 -0
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ cookbooks
19
+ *~
20
+ *.tar*
21
+ \#*
22
+ Cookbookfile*
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p125
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,90 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ knife_cookbook_dependencies (0.0.1)
5
+ chef (~> 0.10.0)
6
+ dep_selector
7
+ minitar
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ bunny (0.7.9)
13
+ chef (0.10.8)
14
+ bunny (>= 0.6.0)
15
+ erubis
16
+ highline
17
+ json (>= 1.4.4, <= 1.6.1)
18
+ mixlib-authentication (>= 1.1.0)
19
+ mixlib-cli (>= 1.1.0)
20
+ mixlib-config (>= 1.1.2)
21
+ mixlib-log (>= 1.3.0)
22
+ moneta
23
+ net-ssh (~> 2.1.3)
24
+ net-ssh-multi (~> 1.1.0)
25
+ ohai (>= 0.6.0)
26
+ rest-client (>= 1.0.4, < 1.7.0)
27
+ treetop (~> 1.4.9)
28
+ uuidtools
29
+ dep_selector (0.0.8)
30
+ diff-lcs (1.1.3)
31
+ erubis (2.7.0)
32
+ highline (1.6.11)
33
+ ipaddress (0.8.0)
34
+ json (1.6.1)
35
+ mime-types (1.18)
36
+ minitar (0.5.3)
37
+ mixlib-authentication (1.1.4)
38
+ mixlib-log
39
+ mixlib-cli (1.2.2)
40
+ mixlib-config (1.1.2)
41
+ mixlib-log (1.3.0)
42
+ moneta (0.6.0)
43
+ multi_json (1.3.4)
44
+ net-ssh (2.1.4)
45
+ net-ssh-gateway (1.1.0)
46
+ net-ssh (>= 1.99.1)
47
+ net-ssh-multi (1.1)
48
+ net-ssh (>= 2.1.4)
49
+ net-ssh-gateway (>= 0.99.0)
50
+ ohai (0.6.12)
51
+ ipaddress
52
+ mixlib-cli
53
+ mixlib-config
54
+ mixlib-log
55
+ systemu
56
+ yajl-ruby
57
+ polyglot (0.3.3)
58
+ rake (0.9.2.2)
59
+ rdoc (3.12)
60
+ json (~> 1.4)
61
+ rest-client (1.6.7)
62
+ mime-types (>= 1.16)
63
+ rspec (2.9.0)
64
+ rspec-core (~> 2.9.0)
65
+ rspec-expectations (~> 2.9.0)
66
+ rspec-mocks (~> 2.9.0)
67
+ rspec-core (2.9.0)
68
+ rspec-expectations (2.9.1)
69
+ diff-lcs (~> 1.1.3)
70
+ rspec-mocks (2.9.0)
71
+ simplecov (0.6.2)
72
+ multi_json (~> 1.3)
73
+ simplecov-html (~> 0.5.3)
74
+ simplecov-html (0.5.3)
75
+ systemu (2.5.0)
76
+ treetop (1.4.10)
77
+ polyglot
78
+ polyglot (>= 0.3.1)
79
+ uuidtools (2.1.2)
80
+ yajl-ruby (1.1.0)
81
+
82
+ PLATFORMS
83
+ ruby
84
+
85
+ DEPENDENCIES
86
+ knife_cookbook_dependencies!
87
+ rake (~> 0.9.0)
88
+ rdoc (~> 3.0)
89
+ rspec
90
+ simplecov
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Josiah Kiehl
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,5 @@
1
+ = Remy, cookbook manager
2
+
3
+ Gusteau:: What do I always say? Anyone can cook.
4
+ Remy:: Yeah. Anyone can, that doesn't mean that anyone should.
5
+ Gusteau:: Well, that is not stopping him. See?
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rdoc/task'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc "check documentation coverage"
6
+ task "rdoc:check" do
7
+ sh "rdoc -C " + Dir["lib/**/*.rb"].join(" ")
8
+ end
9
+
10
+ desc "clean up doco/coverage"
11
+ task :clean do
12
+ sh "rm -fr rdoc coverage"
13
+ end
14
+
15
+ desc "generate documentation"
16
+ RDoc::Task.new :rdoc do |r|
17
+ r.main = "README.rdoc"
18
+ r.rdoc_files.include("README.rdoc", "lib/**/*.rb")
19
+ r.rdoc_dir = "rdoc"
20
+ end
21
+
22
+ desc "Run specs"
23
+ RSpec::Core::RakeTask.new do |t|
24
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
25
+ # Put spec opts in a file named .rspec in root
26
+ end
27
+
28
+ task :check => [:default, "rdoc:check"]
29
+ task :default => [:clean, :spec]
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/knife_cookbook_dependencies/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+
6
+ # TODO FIXME need to modify all of these
7
+
8
+ gem.authors = ["Josiah Kiehl"]
9
+ gem.email = ["josiah@skirmisher.net"]
10
+ gem.description = %q{Resolves cookbook dependencies}
11
+ gem.summary = gem.description
12
+ gem.homepage = "http://github.com/RiotGames/knife_cookbook_dependencies"
13
+
14
+ gem.files = `git ls-files`.split($\)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.name = "knife_cookbook_dependencies"
18
+ gem.require_paths = ["lib"]
19
+ gem.version = KnifeCookbookDependencies::VERSION
20
+
21
+ # FIXME will want to adjust this later
22
+ gem.add_runtime_dependency 'dep_selector', '>= 0'
23
+ gem.add_runtime_dependency 'chef', '~> 0.10.0'
24
+ gem.add_runtime_dependency 'minitar', '>= 0'
25
+
26
+ gem.add_development_dependency 'rake', '~> 0.9.0'
27
+ gem.add_development_dependency 'rspec', '>= 0'
28
+ gem.add_development_dependency 'rdoc', '~> 3.0'
29
+ gem.add_development_dependency 'simplecov', '>= 0'
30
+ end
@@ -0,0 +1,17 @@
1
+ require 'chef/knife'
2
+ require 'knife_cookbook_dependencies'
3
+
4
+ module KnifeCookbookDependencies
5
+ class CookbookDependenciesInstall < Chef::Knife
6
+ banner "knife cookbook dependencies install"
7
+
8
+ def run
9
+ ui.info 'Reading Cookbookfile'
10
+ ::KnifeCookbookDependencies.ui = ui
11
+ ::KnifeCookbookDependencies::Cookbookfile.process_install
12
+ end
13
+ end
14
+
15
+ class CookbookDepsInstall < CookbookDependenciesInstall; end
16
+
17
+ end
@@ -0,0 +1,184 @@
1
+ require 'knife_cookbook_dependencies/knife_utils'
2
+ require 'knife_cookbook_dependencies/git'
3
+ require 'chef/knife/cookbook_site_download'
4
+ require 'chef/knife/cookbook_site_show'
5
+
6
+ module KnifeCookbookDependencies
7
+ class Cookbook
8
+ attr_reader :name, :version_constraints
9
+ attr_accessor :locked_version
10
+
11
+ DOWNLOAD_LOCATION = ENV["TMPDIR"] || '/tmp'
12
+
13
+ def initialize *args
14
+ @options = args.last.is_a?(Hash) ? args.pop : {}
15
+
16
+ if from_git? and from_path?
17
+ raise "Invalid: path and git options provided to #{args[0]}. They are mutually exclusive."
18
+ end
19
+
20
+ @options[:path] = File.expand_path(@options[:path]) if from_path?
21
+ @name, constraint_string = args
22
+
23
+ add_version_constraint(if from_path?
24
+ "= #{version_from_metadata_file.to_s}"
25
+ else
26
+ constraint_string
27
+ end)
28
+ @locked_version = DepSelector::Version.new(@options[:locked_version]) if @options[:locked_version]
29
+ end
30
+
31
+ def add_version_constraint constraint_string
32
+ @version_constraints ||= []
33
+ @version_constraints << DepSelector::VersionConstraint.new(constraint_string) unless @version_constraints.collect(&:to_s).include? constraint_string
34
+ end
35
+
36
+ def download(show_output = false)
37
+ return if @downloaded
38
+ return if !from_git? and downloaded_archive_exists?
39
+
40
+ if from_git?
41
+ @git ||= KnifeCookbookDependencies::Git.new(@options[:git])
42
+ @git.clone
43
+ @git.checkout(@options[:ref]) if @options[:ref]
44
+ @options[:path] ||= @git.directory
45
+ elsif from_path?
46
+ return
47
+ else
48
+ csd = Chef::Knife::CookbookSiteDownload.new([name, latest_constrained_version.to_s, "--file", download_filename])
49
+ output = KnifeCookbookDependencies::KnifeUtils.capture_knife_output(csd)
50
+
51
+ if show_output
52
+ puts output
53
+ end
54
+ end
55
+
56
+ @downloaded = true
57
+ end
58
+
59
+ def copy_to_cookbooks_directory
60
+ FileUtils.mkdir_p KnifeCookbookDependencies::COOKBOOKS_DIRECTORY
61
+
62
+ target = File.join(KnifeCookbookDependencies::COOKBOOKS_DIRECTORY, @name)
63
+ FileUtils.rm_rf target
64
+ FileUtils.cp_r full_path, target
65
+ FileUtils.rm_rf File.join(target, '.git') if from_git?
66
+ end
67
+
68
+ # TODO: Clean up download repetition functionality here, in #download and the associated test.
69
+ def unpack(location = unpacked_cookbook_path, do_clean = false, do_download = true)
70
+ return true if from_path?
71
+ self.clean(File.join(location, @name)) if do_clean
72
+ download if do_download
73
+ fname = download_filename
74
+ if File.directory? location
75
+ true # noop
76
+ elsif downloaded_archive_exists?
77
+ Archive::Tar::Minitar.unpack(Zlib::GzipReader.new(File.open(fname)), location)
78
+ true
79
+ else
80
+ # TODO: Raise friendly error message class
81
+ raise "Archive hasn't been downloaded yet"
82
+ end
83
+ end
84
+
85
+ def dependencies
86
+ download
87
+ unpack
88
+ @dependencies ||= DependencyReader.new(self).read
89
+ end
90
+
91
+ def latest_constrained_version
92
+ return @locked_version if @locked_version
93
+ return version_from_metadata_file if from_path? or from_git?
94
+
95
+ versions.reverse.each do |v|
96
+ return v if version_constraints_include? v
97
+ end
98
+ KnifeCookbookDependencies.ui.fatal "No version available to fit the following constraints for #{@name}: #{version_constraints.inspect}\nAvailable versions: #{versions.inspect}"
99
+ exit 1
100
+ end
101
+
102
+ def version_constraints_include? version
103
+ @version_constraints.inject(true) { |check, constraint| check and constraint.include? version }
104
+ end
105
+
106
+ def versions
107
+ return [latest_constrained_version] if @locked_version
108
+ return [version_from_metadata_file] if from_path? or from_git?
109
+ cookbook_data['versions'].collect { |v| DepSelector::Version.new(v.split(/\//).last.gsub(/_/, '.')) }.sort
110
+ end
111
+
112
+ def version_from_metadata_file
113
+ # TODO: make a generic metadata file reader to replace
114
+ # dependencyreader and incorporate pulling the version as
115
+ # well... knife probably has something like this I can use/steal
116
+ DepSelector::Version.new(metadata_file.match(/version\s+[\"\']([0-9\.]*)[\"\']/)[1])
117
+ end
118
+
119
+ def cookbook_data
120
+ css = Chef::Knife::CookbookSiteShow.new([@name])
121
+ @cookbook_data ||= JSON.parse(KnifeCookbookDependencies::KnifeUtils.capture_knife_output(css))
122
+ end
123
+
124
+ def download_filename
125
+ return nil if from_path?
126
+ File.join(DOWNLOAD_LOCATION, "#{@name}-#{latest_constrained_version}.tar.gz")
127
+ end
128
+
129
+ def unpacked_cookbook_path
130
+ @options[:path] || File.join(File.dirname(download_filename), File.basename(download_filename, '.tar.gz'))
131
+ end
132
+
133
+ def full_path
134
+ if @git
135
+ unpacked_cookbook_path
136
+ else
137
+ File.join(unpacked_cookbook_path, @name)
138
+ end
139
+ end
140
+
141
+ def metadata_filename
142
+ File.join(full_path, "metadata.rb")
143
+ end
144
+
145
+ def metadata_file
146
+ download
147
+ unpack
148
+ File.open(metadata_filename).read
149
+ end
150
+
151
+ def from_path?
152
+ !!@options[:path]
153
+ end
154
+
155
+ def from_git?
156
+ !!@options[:git]
157
+ end
158
+
159
+ def git_repo
160
+ @options[:git]
161
+ end
162
+
163
+ def git_ref
164
+ (from_git? && @git) ? @git.ref : nil
165
+ end
166
+
167
+ def downloaded_archive_exists?
168
+ download_filename && File.exists?(download_filename)
169
+ end
170
+
171
+ def clean(location = unpacked_cookbook_path)
172
+ if @git
173
+ @git.clean
174
+ else
175
+ FileUtils.rm_rf location
176
+ FileUtils.rm_f download_filename
177
+ end
178
+ end
179
+
180
+ def == other
181
+ other.name == @name and other.version_constraints == @version_constraints
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,33 @@
1
+ require 'knife_cookbook_dependencies/dsl'
2
+
3
+ module KnifeCookbookDependencies
4
+ class Cookbookfile
5
+ class << self
6
+ include DSL
7
+ def read content
8
+ # This will populate KnifeCookbookDependencies.shelf. TODO: consider making this
9
+ # build and return the shelf rather than building the shelf as
10
+ # a side effect.
11
+ instance_eval(content)
12
+ end
13
+
14
+ def process_install
15
+ # TODO: friendly error message when the file doesn't exist
16
+
17
+ filename = KnifeCookbookDependencies::DEFAULT_FILENAME + ".lock"
18
+ lockfile = false
19
+
20
+ if File.exist?(filename)
21
+ lockfile = true
22
+ else
23
+ filename = KnifeCookbookDependencies::DEFAULT_FILENAME unless File.exist?(filename)
24
+ end
25
+
26
+ read File.open(filename).read
27
+ KnifeCookbookDependencies.shelf.resolve_dependencies
28
+ KnifeCookbookDependencies.shelf.populate_cookbooks_directory
29
+ KnifeCookbookDependencies.shelf.write_lockfile unless lockfile
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,46 @@
1
+ module KnifeCookbookDependencies
2
+ class DependencyReader
3
+ attr_reader :dependency_list, :cookbook
4
+
5
+ def initialize(cookbook)
6
+ @cookbook = cookbook
7
+ @dependency_list = []
8
+ end
9
+
10
+ def read
11
+ Dir.chdir(@cookbook.full_path) do
12
+ # XXX this filename is required because it sets __FILE__, which is
13
+ # used for README.md parsing among other things in metadata.rb files
14
+ instance_eval(@cookbook.metadata_file, @cookbook.metadata_filename)
15
+ end
16
+ @dependency_list
17
+ end
18
+
19
+ def depends(*args)
20
+ name, constraint = args
21
+
22
+ dependency_cookbook = KnifeCookbookDependencies.shelf.get_cookbook(name) || get_dependency(name)
23
+ if dependency_cookbook
24
+ dependency_cookbook.add_version_constraint constraint
25
+ else
26
+ @dependency_list << Cookbook.new(*args)
27
+ end
28
+ end
29
+
30
+ def get_dependency(name)
31
+ @dependency_list.select { |c| c.name == name }.first
32
+ end
33
+
34
+ def method_missing(method, *args)
35
+ # Don't blow up when other metadata DSL methods are called, we
36
+ # only care about #depends.
37
+ end
38
+
39
+ def name(the_name)
40
+ # Module#name is defined, so method_missing won't catch it
41
+ # when running instance_eval on a metadata.rb that overrides
42
+ # the name
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,7 @@
1
+ module KnifeCookbookDependencies
2
+ module DSL
3
+ def cookbook *args
4
+ KnifeCookbookDependencies.shelf.shelve_cookbook *args
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,86 @@
1
+ require 'tempfile'
2
+
3
+ module KnifeCookbookDependencies
4
+ class Git
5
+ class << self
6
+ def git
7
+ @git ||= find_git
8
+ end
9
+
10
+ #
11
+ # This is to defeat aliases/shell functions called 'git' and a number of
12
+ # other problems.
13
+ #
14
+ def find_git
15
+ git_path = nil
16
+ ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
17
+ potential_path = File.join(path, 'git')
18
+ if File.executable?(potential_path)
19
+ git_path = potential_path
20
+ break
21
+ end
22
+ end
23
+
24
+ unless git_path
25
+ raise "Could not find git. Please ensure it is in your path."
26
+ end
27
+
28
+ return git_path
29
+ end
30
+ end
31
+
32
+ attr_reader :directory
33
+ attr_reader :repository
34
+
35
+ def initialize(repo)
36
+ @repository = repo
37
+ end
38
+
39
+ def clone
40
+ # XXX not sure how resilient this is, maybe a fetch/merge strategy would be better.
41
+ if @directory
42
+ Dir.chdir @directory do
43
+ system(self.class.git, "pull")
44
+ end
45
+ else
46
+ @directory = Dir.mktmpdir
47
+ system(self.class.git, "clone", @repository, @directory)
48
+ end
49
+
50
+ error_check
51
+
52
+ end
53
+
54
+ def checkout(ref)
55
+ clone
56
+
57
+ Dir.chdir @directory do
58
+ system(self.class.git, "checkout", "-q", ref)
59
+ end
60
+
61
+ error_check
62
+ end
63
+
64
+ def ref
65
+ return nil unless @directory
66
+
67
+ this_ref = nil
68
+
69
+ Dir.chdir @directory do
70
+ this_ref = `#{self.class.git} rev-parse HEAD`.strip
71
+ end
72
+
73
+ return this_ref
74
+ end
75
+
76
+ def clean
77
+ FileUtils.rm_rf @directory if @directory
78
+ end
79
+
80
+ def error_check
81
+ if $?.exitstatus != 0
82
+ raise "Did not succeed executing git; check the output above."
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,13 @@
1
+ require 'stringio'
2
+ require 'chef/config'
3
+
4
+ module KnifeCookbookDependencies
5
+ module KnifeUtils
6
+ def self.capture_knife_output(knife_obj)
7
+ knife_obj.ui = Chef::Knife::UI.new(StringIO.new, StringIO.new, StringIO.new, { :format => :json })
8
+ knife_obj.run
9
+ knife_obj.ui.stdout.rewind
10
+ knife_obj.ui.stdout.read
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ require 'knife_cookbook_dependencies/cookbookfile'
2
+
3
+ module KnifeCookbookDependencies
4
+ class Lockfile
5
+ def initialize(cookbooks)
6
+ @cookbooks = cookbooks
7
+ end
8
+
9
+ def write(filename = KnifeCookbookDependencies::DEFAULT_FILENAME)
10
+ content = @cookbooks.map do |cookbook|
11
+ get_cookbook_definition(cookbook)
12
+ end.join("\n")
13
+ File.write(filename + ".lock", content)
14
+ end
15
+
16
+ def get_cookbook_definition(cookbook)
17
+ definition = "cookbook '#{cookbook.name}', :locked_version => '#{cookbook.locked_version}'"
18
+ if cookbook.git_repo
19
+ definition += ", :git => '#{cookbook.git_repo}', :ref => '#{cookbook.git_ref || 'HEAD'}'"
20
+ end
21
+
22
+ return definition
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ module KnifeCookbookDependencies
2
+ class MetaCookbook
3
+ attr_reader :name, :dependencies
4
+
5
+ def initialize name, dependencies
6
+ @name = name
7
+ @dependencies = dependencies
8
+ end
9
+
10
+ def versions
11
+ @v ||= [DepSelector::Version.new('1.0.0')]
12
+ end
13
+
14
+ def latest_constrained_version
15
+ versions.first
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,70 @@
1
+ require 'knife_cookbook_dependencies/lockfile'
2
+
3
+ module KnifeCookbookDependencies
4
+ class Shelf
5
+ META_COOKBOOK_NAME = 'cookbook_dependencies_shelf'
6
+
7
+ attr_accessor :cookbooks
8
+
9
+ def initialize
10
+ @cookbooks = []
11
+ end
12
+
13
+ def shelve_cookbook(*args)
14
+ @cookbooks << (args.first.is_a?(Cookbook) ? args.first : Cookbook.new(*args))
15
+ end
16
+
17
+ def resolve_dependencies
18
+ graph = DepSelector::DependencyGraph.new
19
+
20
+ # all cookbooks in the Cookbookfile are dependencies of the shelf
21
+ shelf = MetaCookbook.new(META_COOKBOOK_NAME, @cookbooks)
22
+
23
+ self.class.populate_graph graph, shelf
24
+
25
+ selector = DepSelector::Selector.new(graph)
26
+ solution = selector.find_solution([DepSelector::SolutionConstraint.new(graph.package(META_COOKBOOK_NAME))])
27
+ solution.delete META_COOKBOOK_NAME
28
+ solution
29
+ end
30
+
31
+ def write_lockfile
32
+ KnifeCookbookDependencies::Lockfile.new(@cookbooks).write
33
+ end
34
+
35
+ def get_cookbook(name)
36
+ @cookbooks.select { |c| c.name == name }.first
37
+ end
38
+
39
+ def populate_cookbooks_directory
40
+ cookbooks_from_path = @cookbooks.select(&:from_path?) | @cookbooks.select(&:from_git?)
41
+
42
+ resolve_dependencies.each_pair do |cookbook_name, version|
43
+ cookbook = cookbooks_from_path.select { |c| c.name == cookbook_name }.first || Cookbook.new(cookbook_name, version.to_s)
44
+ @cookbooks << cookbook
45
+ cookbook.download
46
+ cookbook.unpack
47
+ cookbook.copy_to_cookbooks_directory
48
+ cookbook.locked_version = version
49
+ end
50
+ @cookbooks = @cookbooks.uniq.reject { |x| x.locked_version.nil? }
51
+ end
52
+
53
+ class << self
54
+ def populate_graph(graph, cookbook)
55
+ package = graph.package cookbook.name
56
+ cookbook.versions.each { |v| package.add_version(v) }
57
+ cookbook.dependencies.each do |dependency|
58
+ graph = populate_graph(graph, dependency)
59
+ dep = graph.package(dependency.name)
60
+ version = package.versions.select { |v| v.version == cookbook.latest_constrained_version }.first
61
+ dependency.version_constraints.each do |constraint|
62
+ version.dependencies << DepSelector::Dependency.new(dep, constraint)
63
+ end
64
+ end
65
+
66
+ graph
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ module KnifeCookbookDependencies
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,42 @@
1
+ require 'dep_selector'
2
+ require 'zlib'
3
+ require 'archive/tar/minitar'
4
+ require 'chef/config'
5
+ require 'chef/knife/cookbook_site_download'
6
+
7
+ require 'knife_cookbook_dependencies/version'
8
+ require 'knife_cookbook_dependencies/shelf'
9
+ require 'knife_cookbook_dependencies/cookbook'
10
+ require 'knife_cookbook_dependencies/metacookbook'
11
+ require 'knife_cookbook_dependencies/dependency_reader'
12
+ require 'knife_cookbook_dependencies/dsl'
13
+ require 'knife_cookbook_dependencies/cookbookfile'
14
+ require 'knife_cookbook_dependencies/git'
15
+
16
+ module KnifeCookbookDependencies
17
+ DEFAULT_FILENAME = 'Cookbookfile'
18
+ COOKBOOKS_DIRECTORY = 'cookbooks'
19
+
20
+ class << self
21
+ def shelf
22
+ @shelf ||= KnifeCookbookDependencies::Shelf.new
23
+ end
24
+
25
+ def clear_shelf!
26
+ @shelf = nil
27
+ end
28
+
29
+ def ui=(ui)
30
+ @ui = ui
31
+ end
32
+
33
+ def ui
34
+ @ui ||= Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {})
35
+ end
36
+
37
+ def clean
38
+ clear_shelf!
39
+ FileUtils.rm_rf COOKBOOKS_DIRECTORY
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,12 @@
1
+ Description
2
+ ===========
3
+
4
+ Requirements
5
+ ============
6
+
7
+ Attributes
8
+ ==========
9
+
10
+ Usage
11
+ =====
12
+
@@ -0,0 +1,6 @@
1
+ maintainer "Josiah Kiehl"
2
+ maintainer_email "josiah@skirmisher.net"
3
+ license "DO WHAT YOU WANT CAUSE A PIRATE IS FREE"
4
+ description "Installs/Configures example_cookbook"
5
+ long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
6
+ version "0.5.0"
@@ -0,0 +1,8 @@
1
+ #
2
+ # Cookbook Name:: example_cookbook
3
+ # Recipe:: default
4
+ #
5
+ # Copyright 2012, YOUR_COMPANY_NAME
6
+ #
7
+ # All rights reserved - Do Not Redistribute
8
+ #
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ module KnifeCookbookDependencies
4
+ describe Cookbook do
5
+ subject { Cookbook.new('ntp') }
6
+
7
+ after do
8
+ subject.clean
9
+ end
10
+
11
+ describe do
12
+
13
+ before do
14
+ Cookbook.any_instance.stub(:versions).and_return ['0.1.1', '0.9.0', '1.0.0', '1.1.8'].collect {|v| Gem::Version.new(v) }
15
+ end
16
+
17
+ # FIXME: This test is flakey
18
+ it "should raise an error if the cookbook is unpacked without being downloaded first" do
19
+ -> { subject.unpack(subject.unpacked_cookbook_path, true, false) }.should raise_error
20
+ end
21
+
22
+ describe '#unpacked_cookbook_path' do
23
+ it "should give the path to the directory where the archive should get unpacked" do
24
+ subject.unpacked_cookbook_path.should == File.join(ENV['TMPDIR'], 'ntp-1.1.8')
25
+ end
26
+ end
27
+
28
+ it 'should treat cookbooks pulled from a path like a cookbook that has already been unpacked with the path as the unpacked location' do
29
+ example_cookbook_from_path.unpacked_cookbook_path.should == File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec", "fixtures", "cookbooks"))
30
+ end
31
+
32
+ it "should not attempt to download a cookbook being pulled from a path" do
33
+ Chef::Knife::CookbookSiteDownload.any_instance.should_not_receive(:run)
34
+ example_cookbook_from_path.download
35
+ end
36
+
37
+ describe "#copy_to" do
38
+ it "should copy from the unpacked cookbook directory to the target" do
39
+ example_cookbook_from_path.copy_to_cookbooks_directory
40
+ File.exists?(File.join(KnifeCookbookDependencies::COOKBOOKS_DIRECTORY, example_cookbook_from_path.name)).should be_true
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ describe "#versions" do
47
+ it "should return the version in the metadata file as the available versions for a path sourced cookbook" do
48
+ example_cookbook_from_path.versions.should == [DepSelector::Version.new('0.5.0')]
49
+ end
50
+ end
51
+
52
+ describe '#version_from_metadata_file' do
53
+ it "should be able to handle single quoted strings" do
54
+ Cookbook.any_instance.stub(:metadata_file).and_return <<M
55
+ version '1.2.3'
56
+ M
57
+ subject.version_from_metadata_file.should == DepSelector::Version.new('1.2.3')
58
+ end
59
+ it "should be able to handle double quoted strings" do
60
+ Cookbook.any_instance.stub(:metadata_file).and_return <<M
61
+ version "1.2.3"
62
+ M
63
+ subject.version_from_metadata_file.should == DepSelector::Version.new('1.2.3')
64
+ end
65
+ end
66
+
67
+ describe '#version_constraints_include?' do
68
+ it "should cycle through all the version constraints to confirm that all of them are satisfied" do
69
+ subject.add_version_constraint ">= 1.0.0"
70
+ subject.add_version_constraint "< 2.0.0"
71
+ subject.version_constraints_include?(DepSelector::Version.new('1.0.0')).should be_true
72
+ subject.version_constraints_include?(DepSelector::Version.new('1.5.0')).should be_true
73
+ subject.version_constraints_include?(DepSelector::Version.new('2.0.0')).should be_false
74
+ end
75
+ end
76
+
77
+ describe '#add_version_constraint' do
78
+ it "should not duplicate version constraints" do
79
+ subject.add_version_constraint ">= 1.0.0"
80
+ subject.add_version_constraint ">= 1.0.0"
81
+ subject.add_version_constraint ">= 1.0.0"
82
+ subject.add_version_constraint ">= 1.0.0"
83
+ subject.version_constraints.size.should == 2 # 1 for the
84
+ # default when
85
+ # the cookbook
86
+ # was created in
87
+ # the subject
88
+ # instantiation
89
+ # line
90
+ end
91
+ end
92
+
93
+ describe '#dependencies' do
94
+ it "should not contain the cookbook itself" do
95
+ # TODO: Mock
96
+ Cookbook.new('riot_base', git: 'git@github.riotgames.com:cookbooks/riot_base.git').dependencies.collect(&:name).include?('riot_base').should_not be_true
97
+ end
98
+ end
99
+
100
+ # TODO figure out how to test this. Stubs on classes don't clear after a test.
101
+ # describe '#unpack' do
102
+ # it "should not unpack if it is already unpacked" do
103
+ # Archive::Tar::Minitar.should_receive(:unpack).once
104
+ # subject.unpack
105
+ # subject.unpack
106
+ # end
107
+ # end
108
+ end
109
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module KnifeCookbookDependencies
4
+ describe Cookbookfile do
5
+ describe '::read' do
6
+ after do
7
+ KnifeCookbookDependencies.shelf.cookbooks.each(&:clean)
8
+ end
9
+
10
+ it "should read the cookbookfile and build a dependency list" do
11
+ described_class.read <<COOKBOOKFILE
12
+ cookbook 'ntp', '<= 1.0.0'
13
+ cookbook 'mysql'
14
+ cookbook 'nginx', '< 0.101.2'
15
+ cookbook 'ssh_known_hosts2', :git => 'https://github.com/erikh/chef-ssh_known_hosts2.git'
16
+ COOKBOOKFILE
17
+
18
+ ['ntp', 'mysql', 'nginx', 'ssh_known_hosts2'].each do |dep|
19
+ KnifeCookbookDependencies.shelf.cookbooks.collect(&:name).should include dep
20
+ end
21
+
22
+ KnifeCookbookDependencies.shelf.populate_cookbooks_directory
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ module KnifeCookbookDependencies
4
+ describe DependencyReader do
5
+ subject { DependencyReader.new(Cookbook.new('mysql')) }
6
+
7
+ before do
8
+ @cookbook = Cookbook.new('mysql')
9
+ @cookbook.download # TODO: mock out
10
+ @cookbook.unpack
11
+ end
12
+
13
+ after do
14
+ @cookbook.clean
15
+ end
16
+
17
+ it "should parse the metadata file for dependencies" do
18
+ subject.read.should == [Cookbook.new('openssl')]
19
+ end
20
+
21
+ it "should not blow up when reading a metadata.rb that overrides the name" do
22
+ Cookbook.any_instance.stub(:metadata_file).and_return <<M
23
+ name 'dontblowupplease'
24
+ version '1.2.3'
25
+ M
26
+ -> { subject.read }.should_not raise_error
27
+ end
28
+
29
+ it 'should display a warning when no version is defined in the metadata.rb'
30
+
31
+ it "should add a constraint to the cookbook on the shelf instead of adding a new dependency" do
32
+ KnifeCookbookDependencies.shelf.shelve_cookbook example_cookbook_from_path
33
+ subject.depends('example_cookbook')
34
+ subject.dependency_list.should be_empty
35
+ end
36
+
37
+ it "should create a new cookbook if the cookbook is not found on the shelf" do
38
+ subject.depends('example_cookbook')
39
+ subject.dependency_list.first.name.should == 'example_cookbook'
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'knife_cookbook_dependencies/dsl'
3
+
4
+ module KnifeCookbookDependencies
5
+ describe DSL do
6
+ include DSL
7
+
8
+ describe "#cookbook" do
9
+ after do
10
+ KnifeCookbookDependencies.shelf.cookbooks.each(&:clean)
11
+ end
12
+
13
+ it 'should add the cookbooks to the shelf' do
14
+ cookbook "ntp"
15
+ cookbook "nginx"
16
+
17
+ ['ntp', 'nginx'].each do |cookbook|
18
+ KnifeCookbookDependencies.shelf.cookbooks.collect(&:name).should include cookbook
19
+ end
20
+ end
21
+
22
+ it 'should take version constraints' do
23
+ Cookbook.any_instance.stub(:clean)
24
+ cookbook 'ntp', '= 1.2.3'
25
+ KnifeCookbookDependencies.shelf.cookbooks.select {|c| c.name == 'ntp'}.first.version_constraints.first.should == DepSelector::VersionConstraint.new('= 1.2.3')
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ module KnifeCookbookDependencies
4
+ describe Git do
5
+
6
+ it "should find git" do
7
+ Git.find_git.should_not be_nil
8
+ end
9
+
10
+ it "should raise if it can't find git" do
11
+ begin
12
+ path = ENV["PATH"]
13
+ ENV["PATH"] = ""
14
+
15
+ lambda { Git.find_git }.should raise_error
16
+ ensure
17
+ ENV["PATH"] = path
18
+ end
19
+ end
20
+
21
+ describe "instances" do
22
+ subject { Git.new("https://github.com/erikh/chef-ssh_known_hosts2.git") }
23
+
24
+ after do
25
+ subject.clean
26
+ end
27
+
28
+ it "should be set the repository accessor" do
29
+ subject.repository.should == "https://github.com/erikh/chef-ssh_known_hosts2.git"
30
+ end
31
+
32
+ it "should be able to clone this repository" do
33
+ subject.clone
34
+
35
+ File.exist?(subject.directory).should be_true
36
+ File.directory?(subject.directory).should be_true
37
+ end
38
+
39
+ it "should clone, then pull if the directory is the same" do
40
+ subject.clone
41
+ dir = subject.directory
42
+ subject.clone
43
+ dir2 = subject.directory
44
+
45
+ dir.should == dir2
46
+ end
47
+
48
+ it "should checkout the v0.1.0 tag" do
49
+ lambda { subject.checkout "fart" }.should raise_error
50
+ lambda { subject.checkout "v0.1.0" }.should_not raise_error
51
+
52
+ Dir.chdir subject.directory do
53
+ %x[git rev-parse v0.1.0].should == %x[git rev-parse HEAD]
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module KnifeCookbookDependencies
4
+ describe Shelf do
5
+ describe '#get_cookbook' do
6
+ it "should return nil if a cookbook doesn't exist on the shelf" do
7
+ Shelf.new.get_cookbook('arbitrary').should be_nil
8
+ end
9
+ it "should return the cookbook if the cookbook exists on the shelf" do
10
+ s = Shelf.new
11
+ s.shelve_cookbook example_cookbook_from_path
12
+ s.get_cookbook(example_cookbook_from_path.name).should_not be_nil
13
+ end
14
+ end
15
+
16
+ describe "#shelve_cookbook" do
17
+ subject { Shelf.new }
18
+ it 'should store shelved cookbooks' do
19
+ subject.shelve_cookbook 'acookbook'
20
+ subject.cookbooks.collect(&:name).should include 'acookbook'
21
+ end
22
+
23
+ it 'should take version constraints' do
24
+ subject.shelve_cookbook 'acookbook', '= 1.2.3'
25
+ subject.cookbooks.last.version_constraints.should == [DepSelector::VersionConstraint.new('= 1.2.3')]
26
+ end
27
+
28
+ it "should resolve the dependency graph of the cookbooks on the shelf" do
29
+ subject.shelve_cookbook 'mysql', "= 1.2.4"
30
+
31
+ subject.resolve_dependencies.should == ({"mysql" => DepSelector::Version.new("1.2.4"), "openssl" => DepSelector::Version.new("1.0.0")})
32
+ Cookbook.new('mysql').clean
33
+ Cookbook.new('openssl').clean
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ $:.push File.join(File.dirname(__FILE__), '..')
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'simplecov'
5
+ require 'pp'
6
+
7
+ RSpec.configure do |config|
8
+ config.after do
9
+ KnifeCookbookDependencies.clean
10
+ end
11
+ end
12
+
13
+ SimpleCov.start do
14
+ add_filter 'spec/'
15
+ end
16
+
17
+ require 'knife_cookbook_dependencies'
18
+
19
+ def example_cookbook_from_path
20
+ @example_cookbook_from_path ||= KnifeCookbookDependencies::Cookbook.new('example_cookbook', path: File.join(File.dirname(__FILE__), 'fixtures', 'cookbooks'))
21
+ end
data/todo.txt ADDED
@@ -0,0 +1,13 @@
1
+ Add git support to Cookbookfile
2
+ - Allow a cookbook to be pulled from a git repository
3
+ - Also support 'ref' to give a commit hash to pull
4
+ - Possibly support 'branch' to pull the HEAD from a given branch
5
+ Add path support to Cookbookfile
6
+ - Pull cookbook from a path on the local filesystem
7
+ Add source support to Cookbookfile
8
+ - Allow unofficial cookbook repositories to be used via 'source' in Cookbookfile
9
+ Create local mock API service for specs
10
+ - Instead of mocking out methods, spin up a local mock cookbook repo for tests to run against. This should be able to serve cookbooks as gzips. This might require some monkey patching of knife to get it to talk to the mock server instead of the opscode community site.
11
+ Optimize cookbook retrieval
12
+ - Possible: Pester Opscode till they release an API call to retrieve cookbook dependencies
13
+ - Possible: Keep an index of dependencies so we don't have to download cookbook versions we've seen before
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife_cookbook_dependencies
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Josiah Kiehl
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: dep_selector
16
+ requirement: &70196647928780 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70196647928780
25
+ - !ruby/object:Gem::Dependency
26
+ name: chef
27
+ requirement: &70196647928060 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.10.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70196647928060
36
+ - !ruby/object:Gem::Dependency
37
+ name: minitar
38
+ requirement: &70196647943200 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70196647943200
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &70196647940420 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70196647940420
58
+ - !ruby/object:Gem::Dependency
59
+ name: rspec
60
+ requirement: &70196647936660 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70196647936660
69
+ - !ruby/object:Gem::Dependency
70
+ name: rdoc
71
+ requirement: &70196647944920 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: '3.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70196647944920
80
+ - !ruby/object:Gem::Dependency
81
+ name: simplecov
82
+ requirement: &70196647960000 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70196647960000
91
+ description: Resolves cookbook dependencies
92
+ email:
93
+ - josiah@skirmisher.net
94
+ executables: []
95
+ extensions: []
96
+ extra_rdoc_files: []
97
+ files:
98
+ - .gitignore
99
+ - .rbenv-version
100
+ - Gemfile
101
+ - Gemfile.lock
102
+ - LICENSE
103
+ - README.rdoc
104
+ - Rakefile
105
+ - knife_cookbook_dependencies.gemspec
106
+ - lib/chef/knife/cookbook_dependencies_install.rb
107
+ - lib/knife_cookbook_dependencies.rb
108
+ - lib/knife_cookbook_dependencies/cookbook.rb
109
+ - lib/knife_cookbook_dependencies/cookbookfile.rb
110
+ - lib/knife_cookbook_dependencies/dependency_reader.rb
111
+ - lib/knife_cookbook_dependencies/dsl.rb
112
+ - lib/knife_cookbook_dependencies/git.rb
113
+ - lib/knife_cookbook_dependencies/knife_utils.rb
114
+ - lib/knife_cookbook_dependencies/lockfile.rb
115
+ - lib/knife_cookbook_dependencies/metacookbook.rb
116
+ - lib/knife_cookbook_dependencies/shelf.rb
117
+ - lib/knife_cookbook_dependencies/version.rb
118
+ - spec/fixtures/cookbooks/example_cookbook/README.md
119
+ - spec/fixtures/cookbooks/example_cookbook/metadata.rb
120
+ - spec/fixtures/cookbooks/example_cookbook/recipes/default.rb
121
+ - spec/lib/knife_cookbook_dependencies/cookbook_spec.rb
122
+ - spec/lib/knife_cookbook_dependencies/cookbookfile_spec.rb
123
+ - spec/lib/knife_cookbook_dependencies/dependency_reader_spec.rb
124
+ - spec/lib/knife_cookbook_dependencies/dsl_spec.rb
125
+ - spec/lib/knife_cookbook_dependencies/git_spec.rb
126
+ - spec/lib/knife_cookbook_dependencies/shelf_spec.rb
127
+ - spec/spec_helper.rb
128
+ - todo.txt
129
+ homepage: http://github.com/RiotGames/knife_cookbook_dependencies
130
+ licenses: []
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ! '>='
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubyforge_project:
149
+ rubygems_version: 1.8.11
150
+ signing_key:
151
+ specification_version: 3
152
+ summary: Resolves cookbook dependencies
153
+ test_files:
154
+ - spec/fixtures/cookbooks/example_cookbook/README.md
155
+ - spec/fixtures/cookbooks/example_cookbook/metadata.rb
156
+ - spec/fixtures/cookbooks/example_cookbook/recipes/default.rb
157
+ - spec/lib/knife_cookbook_dependencies/cookbook_spec.rb
158
+ - spec/lib/knife_cookbook_dependencies/cookbookfile_spec.rb
159
+ - spec/lib/knife_cookbook_dependencies/dependency_reader_spec.rb
160
+ - spec/lib/knife_cookbook_dependencies/dsl_spec.rb
161
+ - spec/lib/knife_cookbook_dependencies/git_spec.rb
162
+ - spec/lib/knife_cookbook_dependencies/shelf_spec.rb
163
+ - spec/spec_helper.rb