cookbook-omnifetch 0.1.0

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.
@@ -0,0 +1,8 @@
1
+ module CookbookOmnifetch
2
+ class GithubLocation < GitLocation
3
+ def initialize(dependency, options = {})
4
+ options[:git] = "git://github.com/#{options.delete(:github)}.git"
5
+ super
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,46 @@
1
+ require 'cookbook-omnifetch/exceptions'
2
+
3
+ module CookbookOmnifetch
4
+
5
+ class MissingConfiguration < OmnifetchError; end
6
+
7
+ class NullValue; end
8
+
9
+ class Integration
10
+
11
+ def self.configurables
12
+ @configurables ||= []
13
+ end
14
+
15
+ def self.configurable(name)
16
+ configurables << name
17
+
18
+ attr_writer name
19
+
20
+ define_method(name) do
21
+ value = instance_variable_get("@#{name}".to_sym)
22
+ case value
23
+ when NullValue
24
+ raise MissingConfiguration, "`#{name}` is not configured"
25
+ when Proc
26
+ value.call
27
+ else
28
+ value
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ configurable :cache_path
35
+ configurable :storage_path
36
+ configurable :shell_out_class
37
+ configurable :cached_cookbook_class
38
+
39
+ def initialize
40
+ self.class.configurables.each do |configurable|
41
+ instance_variable_set("@#{configurable}".to_sym, NullValue.new)
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,86 @@
1
+ require 'cookbook-omnifetch/base'
2
+
3
+ module CookbookOmnifetch
4
+ class PathLocation < BaseLocation
5
+ # Technically path locations are always installed, but this method
6
+ # intentionally returns +false+ to force validation of the cookbook at the
7
+ # path.
8
+ #
9
+ # @see BaseLocation#installed?
10
+ def installed?
11
+ false
12
+ end
13
+
14
+ # The installation for a path location is actually just a noop
15
+ #
16
+ # @see BaseLocation#install
17
+ def install
18
+ validate_cached!(expanded_path)
19
+ end
20
+
21
+ # @see BaseLocation#cached_cookbook
22
+ def cached_cookbook
23
+ @cached_cookbook ||= CookbookOmnifetch.cached_cookbook_class.from_path(expanded_path)
24
+ end
25
+
26
+ # Returns true if the location is a metadata location. By default, no
27
+ # locations are the metadata location.
28
+ #
29
+ # @return [Boolean]
30
+ def metadata?
31
+ !!options[:metadata]
32
+ end
33
+
34
+ def install_path
35
+ relative_path
36
+ end
37
+
38
+ # Return this PathLocation's path relative to the associated Berksfile. It
39
+ # is actually the path reative to the associated Berksfile's parent
40
+ # directory.
41
+ #
42
+ # @return [Pathname]
43
+ # the relative path relative to the target
44
+ def relative_path
45
+ # TODO: this requires Berkshelf::Dependency to provide a delegate (ish) method that does
46
+ #
47
+ # def relative_paths_root
48
+ # File.dirname(berksfile.filepath)
49
+ # end
50
+ @relative_path ||= expanded_path.relative_path_from(Pathname.new(dependency.relative_paths_root))
51
+ end
52
+
53
+ # The fully expanded path of this cookbook on disk, relative to the
54
+ # Berksfile.
55
+ #
56
+ # @return [Pathname]
57
+ def expanded_path
58
+ # TODO: this requires Berkshelf::Dependency to provide a delegate (ish) method that does
59
+ #
60
+ # def relative_paths_root
61
+ # File.dirname(berksfile.filepath)
62
+ # end
63
+ @expanded_path ||= Pathname.new File.expand_path(options[:path], dependency.relative_paths_root)
64
+ end
65
+
66
+ def ==(other)
67
+ other.is_a?(PathLocation) &&
68
+ other.metadata? == metadata? &&
69
+ other.relative_path == relative_path
70
+ end
71
+
72
+ def to_lock
73
+ out = " path: #{relative_path}\n"
74
+ out << " metadata: true\n" if metadata?
75
+ out
76
+ end
77
+
78
+ def to_s
79
+ "source at #{relative_path}"
80
+ end
81
+
82
+ def inspect
83
+ "#<CookbookOmnifetch::PathLocation metadata: #{metadata?}, path: #{relative_path}>"
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,3 @@
1
+ module CookbookOmnifetch
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,12 @@
1
+ Description
2
+ ===========
3
+
4
+ Requirements
5
+ ============
6
+
7
+ Attributes
8
+ ==========
9
+
10
+ Usage
11
+ =====
12
+
@@ -0,0 +1,3 @@
1
+ name 'example_cookbook'
2
+ maintainer 'Berkshelf Core'
3
+ 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,2 @@
1
+ .kitchen/
2
+ .kitchen.local.yml
@@ -0,0 +1,26 @@
1
+ ---
2
+ driver_plugin: vagrant
3
+
4
+ platforms:
5
+ - name: ubuntu-12.04
6
+ driver_config:
7
+ box: canonical-ubuntu-12.04
8
+ box_url: http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-amd64-vagrant-disk1.box
9
+ require_chef_omnibus: true
10
+ - name: ubuntu-10.04
11
+ driver_config:
12
+ box: opscode-ubuntu-10.04
13
+ box_url: http://opscode-vm.s3.amazonaws.com/vagrant/opscode_ubuntu-10.04_chef-11.2.0.box
14
+ - name: centos-6.3
15
+ driver_config:
16
+ box: opscode-centos-6.3
17
+ box_url: http://opscode-vm.s3.amazonaws.com/vagrant/opscode_centos-6.3_chef-11.2.0.box
18
+ - name: centos-5.8
19
+ driver_config:
20
+ box: opscode-centos-5.8
21
+ box_url: http://opscode-vm.s3.amazonaws.com/vagrant/opscode_centos-5.8_chef-11.2.0.box
22
+
23
+ suites:
24
+ - name: default
25
+ run_list: []
26
+ attributes: {}
@@ -0,0 +1 @@
1
+ site :opscode
@@ -0,0 +1,3 @@
1
+ DEPENDENCIES
2
+
3
+ GRAPH
@@ -0,0 +1,12 @@
1
+ Description
2
+ ===========
3
+
4
+ Requirements
5
+ ============
6
+
7
+ Attributes
8
+ ==========
9
+
10
+ Usage
11
+ =====
12
+
@@ -0,0 +1,3 @@
1
+ name 'example_cookbook'
2
+ maintainer 'Berkshelf Core'
3
+ 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,44 @@
1
+ require 'cookbook-omnifetch'
2
+
3
+ module Fixtures
4
+
5
+ def fixtures_path
6
+ spec_root.join('fixtures')
7
+ end
8
+
9
+ def spec_root
10
+ Pathname.new(File.expand_path(File.dirname(__FILE__)))
11
+ end
12
+
13
+ end
14
+
15
+ module MockShellOut; end
16
+ module MockCachedCookbook; end
17
+
18
+ RSpec.configure do |config|
19
+
20
+ config.raise_errors_for_deprecations!
21
+
22
+ config.include Fixtures
23
+
24
+ config.expect_with :rspec do |c|
25
+ c.syntax = [:expect]
26
+ end
27
+ config.mock_with :rspec do |c|
28
+ c.syntax = [:expect, :should]
29
+ end
30
+
31
+ config.filter_run :focus => true
32
+
33
+ config.run_all_when_everything_filtered = true
34
+
35
+ config.before(:suite) do
36
+ CookbookOmnifetch.configure do |c|
37
+ c.cache_path = File.expand_path('~/.berkshelf')
38
+ c.storage_path = Pathname.new(File.expand_path('~/.berkshelf/cookbooks'))
39
+ c.shell_out_class = MockShellOut
40
+ c.cached_cookbook_class = MockCachedCookbook
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'cookbook-omnifetch/artifactserver'
3
+
4
+ module CookbookOmnifetch
5
+ describe ArtifactserverLocation do
6
+
7
+ let(:cookbook_name) { "nginx" }
8
+
9
+ let(:cookbook_version) { "1.5.23" }
10
+
11
+ let(:constraint) { double("Constraint") }
12
+
13
+ let(:dependency) { double("Dependency", name: cookbook_name, constraint: constraint) }
14
+
15
+ let(:options) { {artifactserver: "https://cookbooks.opscode.com/api/v1", version: cookbook_version } }
16
+
17
+ subject(:public_repo_location) { ArtifactserverLocation.new(dependency, options) }
18
+
19
+ it "has a URI" do
20
+ expect(public_repo_location.uri).to eq("https://cookbooks.opscode.com/api/v1")
21
+ end
22
+
23
+ it "has a repo host" do
24
+ expect(public_repo_location.repo_host).to eq("cookbooks.opscode.com")
25
+ end
26
+
27
+ it "has an exact version" do
28
+ expect(public_repo_location.cookbook_version).to eq("1.5.23")
29
+ end
30
+
31
+ it "has a cache key containing the site URI and version" do
32
+ expect(public_repo_location.cache_key).to eq("nginx-1.5.23-cookbooks.opscode.com")
33
+ end
34
+
35
+ it "sets the install location as the cache path plus cache key" do
36
+ expected_install_path = Pathname.new('~/.berkshelf/cookbooks').expand_path.join("nginx-1.5.23-cookbooks.opscode.com")
37
+ expect(public_repo_location.install_path).to eq(expected_install_path)
38
+ end
39
+
40
+ it "considers the cookbook installed if it exists in the main cache" do
41
+ expect(public_repo_location.install_path).to receive(:exist?).and_return(true)
42
+ expect(public_repo_location.installed?).to be true
43
+ end
44
+
45
+ it "considers the cookbook not installed if it doesn't exist in the main cache" do
46
+ expect(public_repo_location.install_path).to receive(:exist?).and_return(false)
47
+ expect(public_repo_location.installed?).to be false
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+ require 'cookbook-omnifetch/base'
3
+
4
+ module CookbookOmnifetch
5
+ describe BaseLocation do
6
+ let(:constraint) { double('constraint') }
7
+ let(:dependency) { double('dependency', name: 'cookbook', version_constraint: constraint) }
8
+
9
+ subject { described_class.new(dependency) }
10
+
11
+ describe '#installed?' do
12
+ it 'is an abstract function' do
13
+ expect { subject.installed? }.to raise_error(AbstractFunction)
14
+ end
15
+ end
16
+
17
+ describe '#install' do
18
+ it 'is an abstract function' do
19
+ expect { subject.install }.to raise_error(AbstractFunction)
20
+ end
21
+ end
22
+
23
+ describe '#cached_cookbook' do
24
+ it 'is an abstract function' do
25
+ expect { subject.cached_cookbook }.to raise_error(AbstractFunction)
26
+ end
27
+ end
28
+
29
+ describe '#to_lock' do
30
+ it 'is an abstract function' do
31
+ expect { subject.to_lock }.to raise_error(AbstractFunction)
32
+ end
33
+ end
34
+
35
+ describe '#validate_cached!' do
36
+ context 'when the path is not a cookbook' do
37
+ before { CookbookOmnifetch.stub(:cookbook?).and_return(false) }
38
+
39
+ it 'raises an error' do
40
+ expect {
41
+ subject.validate_cached!('/foo/bar')
42
+ }.to raise_error(NotACookbook)
43
+ end
44
+ end
45
+
46
+ context 'when the path is a cookbook' do
47
+ let(:cookbook) do
48
+ double('cookbook',
49
+ cookbook_name: 'cookbook',
50
+ version: '0.1.0',
51
+ )
52
+ end
53
+
54
+ before do
55
+ CookbookOmnifetch.stub(:cookbook?).and_return(true)
56
+ MockCachedCookbook.stub(:from_path).and_return(cookbook)
57
+ end
58
+
59
+ it 'raises an error if the constraint does not satisfy' do
60
+ constraint.stub(:satisfies?).with('0.1.0').and_return(false)
61
+ expect {
62
+ subject.validate_cached!(cookbook)
63
+ }.to raise_error(CookbookValidationFailure)
64
+ end
65
+
66
+ it 'raises an error if the names do not match' do
67
+ constraint.stub(:satisfies?).with('0.1.0').and_return(true)
68
+ cookbook.stub(:cookbook_name).and_return('different_name')
69
+ expect {
70
+ subject.validate_cached!(cookbook)
71
+ }.to raise_error(MismatchedCookbookName)
72
+ end
73
+
74
+ it 'returns true when the validation succeeds' do
75
+ constraint.stub(:satisfies?).with('0.1.0').and_return(true)
76
+ expect(subject.validate_cached!(cookbook)).to be true
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,258 @@
1
+ require 'spec_helper'
2
+ require 'cookbook-omnifetch/git'
3
+
4
+ module CookbookOmnifetch
5
+ describe GitLocation do
6
+ let(:dependency) { double(name: 'bacon') }
7
+
8
+ subject do
9
+ described_class.new(dependency, git: 'https://repo.com', branch: 'ham',
10
+ tag: 'v1.2.3', ref: 'abc123', revision: 'defjkl123456', rel: 'hi')
11
+ end
12
+
13
+ describe '.initialize' do
14
+ it 'sets the uri' do
15
+ instance = described_class.new(dependency, git: 'https://repo.com')
16
+ expect(instance.uri).to eq('https://repo.com')
17
+ end
18
+
19
+ it 'sets the branch' do
20
+ instance = described_class.new(dependency,
21
+ git: 'https://repo.com', branch: 'magic_new_feature')
22
+ expect(instance.branch).to eq('magic_new_feature')
23
+ end
24
+
25
+ it 'sets the tag' do
26
+ instance = described_class.new(dependency,
27
+ git: 'https://repo.com', tag: 'v1.2.3')
28
+ expect(instance.tag).to eq('v1.2.3')
29
+ end
30
+
31
+ it 'adds the ref' do
32
+ instance = described_class.new(dependency,
33
+ git: 'https://repo.com', ref: 'abc123')
34
+ expect(instance.ref).to eq('abc123')
35
+ end
36
+
37
+ it 'sets the revision' do
38
+ instance = described_class.new(dependency,
39
+ git: 'https://repo.com', revision: 'abcde12345')
40
+ expect(instance.revision).to eq('abcde12345')
41
+ end
42
+
43
+ it 'sets the rel' do
44
+ instance = described_class.new(dependency,
45
+ git: 'https://repo.com', rel: 'internal/path')
46
+ expect(instance.rel).to eq('internal/path')
47
+ end
48
+
49
+ context 'rev_parse' do
50
+ def rev_parse(instance)
51
+ instance.instance_variable_get(:@rev_parse)
52
+ end
53
+
54
+ it 'uses the :ref option with priority' do
55
+ instance = described_class.new(dependency,
56
+ git: 'https://repo.com', ref: 'abc123', branch: 'magic_new_feature')
57
+ expect(rev_parse(instance)).to eq('abc123')
58
+ end
59
+
60
+ it 'uses the :branch option with priority' do
61
+ instance = described_class.new(dependency,
62
+ git: 'https://repo.com', branch: 'magic_new_feature', tag: 'v1.2.3')
63
+ expect(rev_parse(instance)).to eq('magic_new_feature')
64
+ end
65
+
66
+ it 'uses the :tag option' do
67
+ instance = described_class.new(dependency,
68
+ git: 'https://repo.com', tag: 'v1.2.3')
69
+ expect(rev_parse(instance)).to eq('v1.2.3')
70
+ end
71
+
72
+ it 'uses "master" when none is given' do
73
+ instance = described_class.new(dependency, git: 'https://repo.com')
74
+ expect(rev_parse(instance)).to eq('master')
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "#cache_key" do
80
+
81
+ it "concatenates the name and revision" do
82
+ subject.stub(:revision).and_return("abcdef123456")
83
+ expect(subject.cache_key).to eq("bacon-abcdef123456")
84
+ end
85
+ end
86
+
87
+ describe '#installed?' do
88
+ it 'returns false when there is no revision' do
89
+ subject.stub(:revision).and_return(nil)
90
+ expect(subject.installed?).to be false
91
+ end
92
+
93
+ it 'returns false when the install_path does not exist' do
94
+ subject.stub(:revision).and_return('abcd1234')
95
+ subject.stub(:install_path).and_return(double(exist?: false))
96
+ expect(subject.installed?).to be false
97
+ end
98
+
99
+ it 'returns true when the location is installed' do
100
+ subject.stub(:revision).and_return('abcd1234')
101
+ subject.stub(:install_path).and_return(double(exist?: true))
102
+ expect(subject.installed?).to be true
103
+ end
104
+ end
105
+
106
+ describe '#install' do
107
+ before do
108
+ File.stub(:chmod)
109
+ FileUtils.stub(:cp_r)
110
+ subject.stub(:validate_cached!)
111
+ subject.stub(:git)
112
+ end
113
+
114
+ context 'when the repository is cached' do
115
+ it 'pulls a new version' do
116
+ Dir.stub(:chdir) { |args, &b| b.call } # Force eval the chdir block
117
+
118
+ subject.stub(:cached?).and_return(true)
119
+ expect(subject).to receive(:git).with(
120
+ 'fetch --force --tags https://repo.com "refs/heads/*:refs/heads/*"'
121
+ )
122
+ subject.install
123
+ end
124
+ end
125
+
126
+ context 'when the revision is not cached' do
127
+ it 'clones the repository' do
128
+ Dir.stub(:chdir) { |args, &b| b.call } # Force eval the chdir block
129
+
130
+ cache_path = subject.send(:cache_path)
131
+ subject.stub(:cached?).and_return(false)
132
+ expect(subject).to receive(:git).with(
133
+ %|clone https://repo.com "#{cache_path}" --bare --no-hardlinks|
134
+ )
135
+ subject.install
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#cached_cookbook' do
141
+ it 'returns nil if the cookbook is not installed' do
142
+ subject.stub(:installed?).and_return(false)
143
+ expect(subject.cached_cookbook).to be_nil
144
+ end
145
+
146
+ it 'returns the cookbook at the install_path' do
147
+ subject.stub(:installed?).and_return(true)
148
+ MockCachedCookbook.stub(:from_path)
149
+
150
+ expect(MockCachedCookbook).to receive(:from_path).once
151
+ subject.cached_cookbook
152
+ end
153
+ end
154
+
155
+ describe '#==' do
156
+ let(:other) { subject.dup }
157
+
158
+ it 'returns true when everything matches' do
159
+ expect(subject).to eq(other)
160
+ end
161
+
162
+ it 'returns false when the other location is not an GitLocation' do
163
+ other.stub(:is_a?).and_return(false)
164
+ expect(subject).to_not eq(other)
165
+ end
166
+
167
+ it 'returns false when the uri is different' do
168
+ other.stub(:uri).and_return('different')
169
+ expect(subject).to_not eq(other)
170
+ end
171
+
172
+ it 'returns false when the branch is different' do
173
+ other.stub(:branch).and_return('different')
174
+ expect(subject).to_not eq(other)
175
+ end
176
+
177
+ it 'returns false when the tag is different' do
178
+ other.stub(:tag).and_return('different')
179
+ expect(subject).to_not eq(other)
180
+ end
181
+
182
+ it 'returns false when the ref is different' do
183
+ other.stub(:ref).and_return('different')
184
+ expect(subject).to_not eq(other)
185
+ end
186
+
187
+ it 'returns false when the rel is different' do
188
+ other.stub(:rel).and_return('different')
189
+ expect(subject).to_not eq(other)
190
+ end
191
+ end
192
+
193
+ describe '#to_s' do
194
+ it 'prefers the tag' do
195
+ expect(subject.to_s).to eq('https://repo.com (at v1.2.3/hi)')
196
+ end
197
+
198
+ it 'prefers the branch' do
199
+ subject.stub(:tag).and_return(nil)
200
+ expect(subject.to_s).to eq('https://repo.com (at ham/hi)')
201
+ end
202
+
203
+ it 'falls back to the ref' do
204
+ subject.stub(:tag).and_return(nil)
205
+ subject.stub(:branch).and_return(nil)
206
+ expect(subject.to_s).to eq('https://repo.com (at abc123/hi)')
207
+ end
208
+
209
+ it 'does not use the rel if missing' do
210
+ subject.stub(:rel).and_return(nil)
211
+ expect(subject.to_s).to eq('https://repo.com (at v1.2.3)')
212
+ end
213
+ end
214
+
215
+ describe '#to_lock' do
216
+ it 'includes all the information' do
217
+ expect(subject.to_lock).to eq <<-EOH.gsub(/^ {8}/, '')
218
+ git: https://repo.com
219
+ revision: defjkl123456
220
+ ref: abc123
221
+ branch: ham
222
+ tag: v1.2.3
223
+ rel: hi
224
+ EOH
225
+ end
226
+
227
+ it 'does not include the branch if missing' do
228
+ subject.stub(:branch).and_return(nil)
229
+ expect(subject.to_lock).to_not include('branch')
230
+ end
231
+
232
+ it 'does not include the tag if missing' do
233
+ subject.stub(:tag).and_return(nil)
234
+ expect(subject.to_lock).to_not include('tag')
235
+ end
236
+
237
+ it 'does not include the rel if missing' do
238
+ subject.stub(:rel).and_return(nil)
239
+ expect(subject.to_lock).to_not include('rel')
240
+ end
241
+ end
242
+
243
+ describe '#git' do
244
+ before { described_class.send(:public, :git) }
245
+
246
+ it 'raises an error if Git is not installed' do
247
+ CookbookOmnifetch.stub(:which).and_return(false)
248
+ expect { subject.git('foo') }.to raise_error(GitNotInstalled)
249
+ end
250
+
251
+ it 'raises an error if the command fails' do
252
+ shell_out = double('shell_out', success?: false, stderr: nil)
253
+ MockShellOut.stub(:shell_out).and_return(shell_out)
254
+ expect { subject.git('foo') }.to raise_error(GitCommandError)
255
+ end
256
+ end
257
+ end
258
+ end