berkshelf 3.0.0.beta4 → 3.0.0.beta5

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +6 -0
  4. data/README.md +3 -0
  5. data/berkshelf-complete.sh +10 -3
  6. data/berkshelf.gemspec +12 -11
  7. data/features/berksfile.feature +0 -2
  8. data/features/commands/apply.feature +0 -1
  9. data/features/commands/contingent.feature +0 -3
  10. data/features/commands/cookbook.feature +0 -3
  11. data/features/commands/init.feature +0 -2
  12. data/features/commands/install.feature +42 -35
  13. data/features/commands/list.feature +0 -3
  14. data/features/commands/outdated.feature +0 -4
  15. data/features/commands/package.feature +2 -39
  16. data/features/commands/shelf/list.feature +0 -2
  17. data/features/commands/shelf/show.feature +0 -5
  18. data/features/commands/shelf/uninstall.feature +0 -5
  19. data/features/commands/show.feature +0 -3
  20. data/features/commands/update.feature +0 -3
  21. data/features/commands/upload.feature +0 -13
  22. data/features/commands/vendor.feature +0 -5
  23. data/features/community_site.feature +0 -2
  24. data/features/config.feature +0 -6
  25. data/features/json_formatter.feature +0 -5
  26. data/features/licenses.feature +0 -5
  27. data/features/lockfile.feature +0 -20
  28. data/features/step_definitions/chef_server_steps.rb +8 -2
  29. data/generator_files/Gemfile.erb +2 -2
  30. data/lib/berkshelf.rb +0 -4
  31. data/lib/berkshelf/berksfile.rb +21 -51
  32. data/lib/berkshelf/cli.rb +26 -15
  33. data/lib/berkshelf/config.rb +4 -0
  34. data/lib/berkshelf/cookbook_generator.rb +1 -1
  35. data/lib/berkshelf/cookbook_store.rb +8 -0
  36. data/lib/berkshelf/core_ext/file.rb +2 -2
  37. data/lib/berkshelf/core_ext/pathname.rb +5 -7
  38. data/lib/berkshelf/dependency.rb +8 -1
  39. data/lib/berkshelf/downloader.rb +37 -0
  40. data/lib/berkshelf/errors.rb +1 -0
  41. data/lib/berkshelf/formatters.rb +1 -0
  42. data/lib/berkshelf/formatters/human_readable.rb +23 -9
  43. data/lib/berkshelf/formatters/json.rb +7 -9
  44. data/lib/berkshelf/git.rb +0 -1
  45. data/lib/berkshelf/init_generator.rb +1 -1
  46. data/lib/berkshelf/installer.rb +43 -19
  47. data/lib/berkshelf/location.rb +8 -13
  48. data/lib/berkshelf/locations/git_location.rb +12 -19
  49. data/lib/berkshelf/locations/mercurial_location.rb +5 -18
  50. data/lib/berkshelf/locations/path_location.rb +7 -0
  51. data/lib/berkshelf/lockfile.rb +5 -1
  52. data/lib/berkshelf/packager.rb +73 -0
  53. data/lib/berkshelf/resolver.rb +7 -5
  54. data/lib/berkshelf/resolver/graph.rb +7 -0
  55. data/lib/berkshelf/source.rb +22 -2
  56. data/lib/berkshelf/ui.rb +0 -2
  57. data/lib/berkshelf/version.rb +1 -1
  58. data/spec/support/git.rb +1 -0
  59. data/spec/support/mercurial.rb +37 -36
  60. data/spec/unit/berkshelf/berksfile_spec.rb +2 -34
  61. data/spec/unit/berkshelf/cli_spec.rb +3 -2
  62. data/spec/unit/berkshelf/config_spec.rb +3 -3
  63. data/spec/unit/berkshelf/core_ext/pathname_spec.rb +46 -0
  64. data/spec/unit/berkshelf/dependency_spec.rb +3 -3
  65. data/spec/unit/berkshelf/formatters_spec.rb +4 -4
  66. data/spec/unit/berkshelf/git_spec.rb +8 -8
  67. data/spec/unit/berkshelf/installer_spec.rb +2 -2
  68. data/spec/unit/berkshelf/locations/git_location_spec.rb +2 -8
  69. data/spec/unit/berkshelf/locations/mercurial_location_spec.rb +4 -23
  70. data/spec/unit/berkshelf/lockfile_spec.rb +1 -1
  71. data/spec/unit/berkshelf/mercurial_spec.rb +6 -7
  72. data/spec/unit/berkshelf/packager_spec.rb +39 -0
  73. data/spec/unit/berkshelf/ui_spec.rb +2 -2
  74. data/spec/unit/berkshelf_spec.rb +2 -2
  75. metadata +37 -24
  76. data/lib/berkshelf/api_client.rb +0 -65
  77. data/lib/berkshelf/api_client/remote_cookbook.rb +0 -55
  78. data/spec/unit/berkshelf/api_client/remote_cookbook_spec.rb +0 -23
  79. data/spec/unit/berkshelf/api_client_spec.rb +0 -57
@@ -49,6 +49,13 @@ module Berkshelf
49
49
  "./#{new_path}"
50
50
  end
51
51
 
52
+ # Valid if the path exists and is readable
53
+ #
54
+ # @return [Boolean]
55
+ def valid?
56
+ File.exist?(path) && File.readable?(path)
57
+ end
58
+
52
59
  def to_hash
53
60
  super.merge(value: self.path)
54
61
  end
@@ -99,7 +99,9 @@ module Berkshelf
99
99
  options[:path] &&= File.expand_path(options[:path], File.dirname(filepath))
100
100
 
101
101
  begin
102
- add(Berkshelf::Dependency.new(berksfile, name.to_s, options))
102
+ dependency = Berkshelf::Dependency.new(berksfile, name.to_s, options)
103
+ next if dependency.location && !dependency.location.valid?
104
+ add(dependency)
103
105
  rescue Berkshelf::CookbookNotFound
104
106
  # It's possible that a source is locked that contains a path location, and
105
107
  # that path location was renamed or no longer exists. When loading the
@@ -123,6 +125,7 @@ module Berkshelf
123
125
  #
124
126
  # @param [String, Berkshelf::Dependency] dependency
125
127
  # the cookbook dependency/name to find
128
+ #
126
129
  # @return [Berkshelf::Dependency, nil]
127
130
  # the cookbook dependency from this lockfile or nil if one was not found
128
131
  def find(dependency)
@@ -133,6 +136,7 @@ module Berkshelf
133
136
  #
134
137
  # @param [String, Berkshelf::Dependency] dependency
135
138
  # the cookbook dependency/name to determine existence of
139
+ #
136
140
  # @return [Boolean]
137
141
  # true if the dependency exists, false otherwise
138
142
  def has_dependency?(dependency)
@@ -0,0 +1,73 @@
1
+ require 'archive/tar/minitar'
2
+ require 'zlib'
3
+
4
+ module Berkshelf
5
+ # A class for archiving and compressing directory containing one or more cookbooks.
6
+ #
7
+ # Example:
8
+ # Archiving a path containing the cookbooks:
9
+ # * "/path/to/cookbooks/my_face"
10
+ # * "/path/to/cookbooks/nginx"
11
+ #
12
+ # irb> source = "/path/to/cookbooks"
13
+ # irb> packager = Berkshelf::Packager.new("some/path/cookbooks.tar.gz")
14
+ # irb> packager.run(source) #=> "some/path/cookbooks.tar.gz"
15
+ class Packager
16
+ class << self
17
+ def validate_destination(path)
18
+ path = path.to_s
19
+ end
20
+ end
21
+
22
+ # @return [String]
23
+ attr_reader :out_file
24
+
25
+ # @param [#to_s] out_file
26
+ # path to write the archive to
27
+ def initialize(out_file)
28
+ @out_file = out_file.to_s
29
+ @out_dir, @filename = File.split(@out_file)
30
+ end
31
+
32
+ # Archive the contents of given path
33
+ #
34
+ # @param [#to_s] source
35
+ # the filepath to archive the contents of
36
+ #
37
+ # @raise [PackageError]
38
+ # if an error is encountered while writing the out_file
39
+ #
40
+ # @return [String]
41
+ # path to the generated archive
42
+ def run(source)
43
+ Dir.chdir(source.to_s) do |dir|
44
+ tgz = Zlib::GzipWriter.new(File.open(out_file, "wb"))
45
+ Archive::Tar::Minitar.pack(".", tgz)
46
+ end
47
+
48
+ out_file
49
+ rescue SystemCallError => ex
50
+ raise PackageError, ex
51
+ end
52
+
53
+ # Validate that running the packager would be successful. Returns nil if would be
54
+ # successful and raises an error if would not.
55
+ #
56
+ # @raise [PackageError]
57
+ # if running the packager would absolutely not result in a success
58
+ #
59
+ # @return [nil]
60
+ def validate!
61
+ raise PackageError, "Path is not a directory: #{out_dir}" unless File.directory?(out_dir)
62
+ raise PackageError, "Directory is not writable: #{out_dir}" unless File.writable?(out_dir)
63
+ end
64
+
65
+ private
66
+
67
+ # @return [String]
68
+ attr_reader :out_dir
69
+
70
+ # @return [String]
71
+ attr_reader :filename
72
+ end
73
+ end
@@ -41,11 +41,13 @@ module Berkshelf
41
41
  demands.push(demand)
42
42
  end
43
43
 
44
- def add_explicit_dependencies(dependency)
45
- unless cookbook = dependency.cached_cookbook
46
- return nil
47
- end
48
-
44
+ # Add dependencies of a locally cached cookbook which will take precedence over anything
45
+ # found in the universe.
46
+ #
47
+ # @param [Berkshelf::CachedCookbook] cookbook
48
+ #
49
+ # @return [Hash]
50
+ def add_explicit_dependencies(cookbook)
49
51
  graph.populate_local(cookbook)
50
52
  end
51
53
 
@@ -10,6 +10,11 @@ module Berkshelf
10
10
  end
11
11
  end
12
12
 
13
+ # Add dependencies of a locally cached cookbook to the graph
14
+ #
15
+ # @param [Berkshelf::CachedCookbook] cookbook
16
+ #
17
+ # @return [Hash]
13
18
  def populate_local(cookbook)
14
19
  name = cookbook.cookbook_name
15
20
  version = cookbook.version
@@ -23,6 +28,8 @@ module Berkshelf
23
28
  # @param [Array<Berkshelf::Source>, Berkshelf::Source] sources
24
29
  def populate(sources)
25
30
  universe(sources).each do |cookbook|
31
+ next if has_artifact?(cookbook.name, cookbook.version)
32
+
26
33
  artifacts(cookbook.name, cookbook.version)
27
34
 
28
35
  cookbook.dependencies.each do |dependency, constraint|
@@ -1,3 +1,5 @@
1
+ require 'berkshelf/api-client'
2
+
1
3
  module Berkshelf
2
4
  class Source
3
5
  include Comparable
@@ -9,11 +11,29 @@ module Berkshelf
9
11
  def initialize(uri)
10
12
  @uri = SourceURI.parse(uri)
11
13
  @api_client = APIClient.new(uri)
14
+ @universe = nil
15
+ end
16
+
17
+ # Forcefully obtain the universe from the API endpoint and assign it to {#universe}. This
18
+ # will reload the value of {#universe} even if it has been loaded before.
19
+ #
20
+ # @return [Array<APIClient::RemoteCookbook>]
21
+ def build_universe
22
+ @universe = api_client.universe
23
+ rescue => ex
24
+ @universe = Array.new
25
+ raise ex
12
26
  end
13
27
 
14
- # @return [Hash]
28
+ # Return the universe from the API endpoint.
29
+ #
30
+ # This is lazily loaded so the universe will be retrieved from the API endpoint on the first
31
+ # call and cached for future calls. Send the {#build_universe} message if you want to reload
32
+ # the cached universe.
33
+ #
34
+ # @return [Array<APIClient::RemoteCookbook>]
15
35
  def universe
16
- @universe ||= api_client.universe
36
+ @universe || build_universe
17
37
  end
18
38
 
19
39
  # @param [String] name
data/lib/berkshelf/ui.rb CHANGED
@@ -38,8 +38,6 @@ module Berkshelf
38
38
  end
39
39
 
40
40
  def error(message, color = :red)
41
- return if quiet?
42
-
43
41
  message = set_color(message, *color) if color
44
42
  super(message)
45
43
  end
@@ -1,3 +1,3 @@
1
1
  module Berkshelf
2
- VERSION = "3.0.0.beta4"
2
+ VERSION = "3.0.0.beta5"
3
3
  end
data/spec/support/git.rb CHANGED
@@ -82,6 +82,7 @@ module Berkshelf
82
82
  end
83
83
 
84
84
  private
85
+
85
86
  # The path to store the local git clones.
86
87
  #
87
88
  # @return [Pathname]
@@ -77,46 +77,47 @@ module Berkshelf
77
77
  end
78
78
 
79
79
  private
80
- # The path to store the local git clones.
81
- #
82
- # @return [Pathname]
83
- def clones
84
- ensure_and_return(tmp_path.join('clones'))
85
- end
86
80
 
87
- # The path to store the git remotes.
88
- #
89
- # @return [Pathname]
90
- def remotes
91
- ensure_and_return(tmp_path.join('remotes'))
92
- end
81
+ # The path to store the local git clones.
82
+ #
83
+ # @return [Pathname]
84
+ def clones
85
+ ensure_and_return(tmp_path.join('clones'))
86
+ end
93
87
 
94
- # Generate a cookbook by the given name.
95
- #
96
- # @param [#to_s] name
97
- # the name of the cookbook to create
98
- # @param [Hash] options
99
- # the list ooptions to pass to the generator
100
- def generate_mercurial_cookbook(name, options = {})
101
- options = {
102
- skip_vagrant: true,
103
- skip_test_kitchen: true,
104
- force: true,
105
- }.merge(options)
88
+ # The path to store the git remotes.
89
+ #
90
+ # @return [Pathname]
91
+ def remotes
92
+ ensure_and_return(tmp_path.join('remotes'))
93
+ end
106
94
 
107
- Berkshelf::Cli.new.invoke(:cookbook, [name.to_s], options)
108
- end
95
+ # Generate a cookbook by the given name.
96
+ #
97
+ # @param [#to_s] name
98
+ # the name of the cookbook to create
99
+ # @param [Hash] options
100
+ # the list ooptions to pass to the generator
101
+ def generate_mercurial_cookbook(name, options = {})
102
+ options = {
103
+ skip_vagrant: true,
104
+ skip_test_kitchen: true,
105
+ force: true,
106
+ }.merge(options)
109
107
 
110
- # Make sure the given path exists and return the path
111
- #
112
- # @param [#to_s] path
113
- # the path to create and return
114
- #
115
- # @return [Pathname]
116
- def ensure_and_return(path)
117
- FileUtils.mkdir(path) unless File.exist?(path)
118
- return Pathname.new(path).expand_path
119
- end
108
+ Berkshelf::Cli.new.invoke(:cookbook, [name.to_s], options)
109
+ end
110
+
111
+ # Make sure the given path exists and return the path
112
+ #
113
+ # @param [#to_s] path
114
+ # the path to create and return
115
+ #
116
+ # @return [Pathname]
117
+ def ensure_and_return(path)
118
+ FileUtils.mkdir(path) unless File.exist?(path)
119
+ return Pathname.new(path).expand_path
120
+ end
120
121
  end
121
122
  end
122
123
  end
@@ -313,7 +313,7 @@ describe Berkshelf::Berksfile do
313
313
  end
314
314
 
315
315
  context "when given the :git option" do
316
- let(:options) { { git: "git@github.com:RiotGames/berkshelf.git" } }
316
+ let(:options) { { git: "git@github.com:berkshelf/berkshelf.git" } }
317
317
 
318
318
  it "has a GitLocation location" do
319
319
  expect(dependency.location).to be_a(Berkshelf::GitLocation)
@@ -321,7 +321,7 @@ describe Berkshelf::Berksfile do
321
321
  end
322
322
 
323
323
  context "when given the :github option" do
324
- let(:options) { { github: "RiotGames/berkshelf" } }
324
+ let(:options) { { github: "berkshelf/berkshelf" } }
325
325
 
326
326
  it "has a GithubLocation location" do
327
327
  expect(dependency.location).to be_a(Berkshelf::GithubLocation)
@@ -512,38 +512,6 @@ describe Berkshelf::Berksfile do
512
512
  end
513
513
  end
514
514
 
515
- describe '#package' do
516
- context 'when the dependency does not exist' do
517
- it 'raises a CookbookNotFound exception' do
518
- expect {
519
- subject.package('non-existent', output: Dir.tmpdir)
520
- }.to raise_error(Berkshelf::CookbookNotFound)
521
- end
522
- end
523
-
524
- context 'when the dependency exists' do
525
- let(:dependency) { double('dependency') }
526
- let(:cached) { double('cached', path: '/foo/bar', cookbook_name: 'cookbook') }
527
- let(:options) { { output: Dir.tmpdir } }
528
-
529
- before do
530
- FileUtils.stub(:cp_r)
531
- FileUtils.stub(:mkdir_p)
532
- subject.stub(:find).with('non-existent').and_return(dependency)
533
- subject.stub(:install).with(options).and_return([ cached ])
534
- end
535
-
536
- it 'resolves the dependencies' do
537
- subject.should_receive(:install).with(options)
538
- subject.package('non-existent', options)
539
- end
540
-
541
- it 'returns the output path' do
542
- expect(subject.package('non-existent', options)).to eq(File.join(Dir.tmpdir, 'non-existent.tar.gz'))
543
- end
544
- end
545
- end
546
-
547
515
  describe "#remove_dependency" do
548
516
  let(:dependency) { "nginx" }
549
517
  before { subject.add_dependency(dependency) }
@@ -4,12 +4,13 @@ describe Berkshelf::Cli do
4
4
  let(:subject) { described_class.new }
5
5
  let(:berksfile) { double('Berksfile') }
6
6
  let(:cookbooks) { ['mysql'] }
7
+
7
8
  describe '#upload' do
8
9
  it 'calls to upload with params if passed in cli' do
9
10
  Berkshelf::Berksfile.should_receive(:from_file).and_return(berksfile)
10
- berksfile.should_receive(:upload).with(include(:skip_syntax_check => true, :freeze => false, :cookbooks => cookbooks))
11
+ berksfile.should_receive(:upload).with(include(skip_syntax_check: true, freeze: false, cookbooks: cookbooks))
11
12
  subject.options[:skip_syntax_check] = true
12
- subject.options[:no_freeze] = true
13
+ subject.options[:no_freeze] = true
13
14
  subject.upload('mysql')
14
15
  end
15
16
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Berkshelf::Config do
4
- describe '.file' do
4
+ describe '::file' do
5
5
  context 'when the file does not exist' do
6
6
  before { File.stub(:exists?).and_return(false) }
7
7
 
@@ -11,13 +11,13 @@ describe Berkshelf::Config do
11
11
  end
12
12
  end
13
13
 
14
- describe '.instance' do
14
+ describe '::instance' do
15
15
  it 'should be a Berkshelf::Config' do
16
16
  expect(Berkshelf::Config.instance).to be_an_instance_of(Berkshelf::Config)
17
17
  end
18
18
  end
19
19
 
20
- describe '.path' do
20
+ describe '::path' do
21
21
  it 'is a string' do
22
22
  expect(Berkshelf::Config.path).to be_a(String)
23
23
  end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pathname do
4
+ describe "#cookbook?" do
5
+ let(:cookbook_path) { tmp_path }
6
+ let(:metadata_rb) { tmp_path.join("metadata.rb") }
7
+ let(:metadata_json) { tmp_path.join("metadata.json") }
8
+
9
+ subject { Pathname.new(cookbook_path) }
10
+
11
+ context "when the path contains a metadata.json file" do
12
+ before { FileUtils.touch(metadata_json) }
13
+ its(:cookbook?) { should be_true }
14
+ end
15
+
16
+ context "when the path contains a metadata.rb file" do
17
+ before { FileUtils.touch(metadata_rb) }
18
+ its(:cookbook?) { should be_true }
19
+ end
20
+
21
+ context "when the path does not contain a metadata.json or metadata.rb file" do
22
+ before { FileUtils.rm_f(metadata_rb) && FileUtils.rm_f(metadata_json) }
23
+ its(:cookbook?) { should be_false }
24
+ end
25
+ end
26
+
27
+ describe "#cookbook_root" do
28
+ let(:root_path) { fixtures_path.join("cookbooks", "example_cookbook") }
29
+ let(:cookbook_path) { root_path }
30
+ subject { Pathname.new(cookbook_path) }
31
+
32
+ context "when in the root of a cookbook" do
33
+ its(:cookbook_root) { should eql(root_path) }
34
+ end
35
+
36
+ context "when in the structure of a cookbook" do
37
+ let(:cookbook_path) { root_path.join("recipes") }
38
+ its(:cookbook_root) { should eql(root_path) }
39
+ end
40
+
41
+ context "when not within the structure of a cookbook" do
42
+ let(:cookbook_path) { "/" }
43
+ its(:cookbook_root) { should be_nil }
44
+ end
45
+ end
46
+ end