berkshelf 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ # Berkshelf
2
+
3
+ Manage a Cookbook or an Application's Cookbook dependencies
4
+
5
+ ## Installation
6
+
7
+ $ gem install berkshelf
8
+
9
+ ## Usage
10
+
11
+ See [berkshelf.com](http://berkshelf.com) for up-to-date usage instructions.
12
+
13
+ # Contributing
14
+
15
+ ## Running tests
16
+
17
+ ### Install prerequisites
18
+
19
+ Install the latest version of {Bundler}[http://gembundler.com]
20
+
21
+ $ gem install bundler
22
+
23
+ Clone the project
24
+
25
+ $ git clone git://github.com/RiotGames/berkshelf.git
26
+
27
+ and run:
28
+
29
+ $ cd berkshelf
30
+ $ bundle install
31
+
32
+ Bundler will install all gems and their dependencies required for testing and developing.
33
+
34
+ ### Running unit (RSpec) and acceptance (Cucumber) tests
35
+
36
+ $ bundle exec guard start
37
+
38
+ # Authors and Contributors
39
+
40
+ * Josiah Kiehl (<josiah@skirmisher.net>)
41
+ * Jamie Winsor (<jamie@vialstudios.com>)
42
+ * Erik Hollensbe (<erik@hollensbe.org>)
43
+ * Michael Ivey (<ivey@gweezlebur.com>)
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
21
21
  s.add_runtime_dependency 'minitar'
22
22
  s.add_runtime_dependency 'thor', '~> 0.15.2'
23
23
 
24
+ s.add_development_dependency 'redcarpet'
24
25
  s.add_development_dependency 'cucumber'
25
26
  s.add_development_dependency 'vcr'
26
27
  s.add_development_dependency 'webmock'
@@ -53,3 +53,20 @@ Feature: install cookbooks from a Berksfile
53
53
  Cookbook 'doesntexist' not found at site: 'http://cookbooks.opscode.com/api/v1/cookbooks'
54
54
  """
55
55
  And the CLI should exit with the status code for error "DownloadFailure"
56
+
57
+ @wip
58
+ Scenario: running install command with the --shims flag to create a directory of shims
59
+ Given I write to "Berksfile" with:
60
+ """
61
+ cookbook "mysql", "1.2.4"
62
+ """
63
+ When I run the install command with flags:
64
+ | --shims |
65
+ Then the following directories should exist:
66
+ | cookbooks |
67
+ | cookbooks/mysql |
68
+ And the output should contain:
69
+ """
70
+ Shims written to:
71
+ """
72
+ And the exit status should be 0
@@ -39,7 +39,11 @@ When /^I run the init command with no value for the target$/ do
39
39
  end
40
40
 
41
41
  When /^I run the install command$/ do
42
- run_simple(unescape("knife berks install"), false)
42
+ run_simple(unescape("knife berks install"), true)
43
+ end
44
+
45
+ When /^I run the install command with flags:$/ do |flags|
46
+ run_simple(unescape("knife berks install #{flags.raw.join(" ")}"), true)
43
47
  end
44
48
 
45
49
  When /^I run the update command$/ do
@@ -119,9 +119,16 @@ module Berkshelf
119
119
  # @option options [Symbol, Array] :without
120
120
  # Group(s) to exclude which will cause any sources marked as a member of the
121
121
  # group to not be installed
122
+ # @option options [String, Pathname] :shims
123
+ # Path to a directory of shims each pointing to a Cookbook Version that is
124
+ # part of the dependency solution. Each shim is a hard link on disk.
122
125
  def install(options = {})
123
126
  resolver = Resolver.new(Berkshelf.downloader, sources(exclude: options[:without]))
124
- resolver.resolve
127
+ solution = resolver.resolve
128
+ if options[:shims]
129
+ write_shims(options[:shims], solution)
130
+ Berkshelf.ui.info "Shims written to: '#{options[:shims]}'"
131
+ end
125
132
  write_lockfile(resolver.sources) unless lockfile_present?
126
133
  end
127
134
 
@@ -152,6 +159,26 @@ module Berkshelf
152
159
  end
153
160
  end
154
161
 
162
+ # Write a collection of hard links to the given path representing the given
163
+ # CachedCookbooks. Useful for getting Cookbooks in a single location for
164
+ # consumption by Vagrant, or another tool that expect this structure.
165
+ #
166
+ # @example
167
+ # Given the path: '/Users/reset/code/pvpnet/cookbooks'
168
+ # And a CachedCookbook: 'nginx' verison '0.100.5' at '/Users/reset/.berkshelf/nginx-0.100.5'
169
+ #
170
+ # A hardlink will be created at: '/Users/reset/code/pvpnet/cookbooks/nginx'
171
+ #
172
+ # @param [Pathname, String] path
173
+ # @param [Array<Berkshelf::CachedCookbook>] cached_cookbooks
174
+ def write_shims(path, cached_cookbooks)
175
+ FileUtils.mkdir_p(path)
176
+ cached_cookbooks.each do |cached_cookbook|
177
+ destination = File.join(path, cached_cookbook.cookbook_name)
178
+ FileUtils.ln_r(cached_cookbook.path, destination, force: true)
179
+ end
180
+ end
181
+
155
182
  private
156
183
 
157
184
  def lockfile_present?
@@ -40,7 +40,7 @@ module Berkshelf
40
40
 
41
41
  def to_s
42
42
  s = "git: '#{uri}'"
43
- s << " with branch '#{branch}" if branch
43
+ s << " with branch '#{branch}'" if branch
44
44
  s
45
45
  end
46
46
 
@@ -0,0 +1,80 @@
1
+ module FileUtils
2
+ # @note taken from proposed FileUtils feature:
3
+ # @note {http://redmine.ruby-lang.org/issues/show/4189}
4
+ # @note {https://github.com/ruby/ruby/pull/4}
5
+ #
6
+ # Options: noop verbose dereference_root force
7
+ #
8
+ # Hard link +src+ to +dest+. If +src+ is a directory, this method links
9
+ # all its contents recursively. If +dest+ is a directory, links
10
+ # +src+ to +dest/src+.
11
+ #
12
+ # +src+ can be a list of files.
13
+ #
14
+ # # Installing ruby library "mylib" under the site_ruby
15
+ # FileUtils.rm_r site_ruby + '/mylib', :force
16
+ # FileUtils.ln_r 'lib/', site_ruby + '/mylib'
17
+ #
18
+ # # Examples of copying several files to target directory.
19
+ # FileUtils.ln_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
20
+ # FileUtils.ln_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true
21
+ #
22
+ # # If you want to copy all contents of a directory instead of the
23
+ # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
24
+ # # use following code.
25
+ # FileUtils.ln_r 'src/.', 'dest' # cp_r('src', 'dest') makes src/dest,
26
+ # # but this doesn't.
27
+ def ln_r(src, dest, options = {})
28
+ fu_check_options options, OPT_TABLE['ln_r']
29
+ fu_output_message "ln -r#{options[:force] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
30
+ return if options[:noop]
31
+ options = options.dup
32
+ options[:dereference_root] = true unless options.key?(:dereference_root)
33
+ fu_each_src_dest(src, dest) do |s, d|
34
+ link_entry s, d, options[:dereference_root], options[:force]
35
+ end
36
+ end
37
+ module_function :ln_r
38
+
39
+ OPT_TABLE['ln_r'] = [:noop, :verbose, :dereference_root, :force]
40
+
41
+ #
42
+ # Hard links a file system entry +src+ to +dest+.
43
+ # If +src+ is a directory, this method links its contents recursively.
44
+ #
45
+ # Both of +src+ and +dest+ must be a path name.
46
+ # +src+ must exist, +dest+ must not exist.
47
+ #
48
+ # If +dereference_root+ is true, this method dereference tree root.
49
+ #
50
+ # If +force+ is true, this method removes each destination file before copy.
51
+ #
52
+ def link_entry(src, dest, dereference_root = false, force = false)
53
+ Entry_.new(src, nil, dereference_root).traverse do |ent|
54
+ destent = Entry_.new(dest, ent.rel, false)
55
+ File.unlink destent.path if force && File.file?(destent.path)
56
+ ent.link destent.path
57
+ end
58
+ end
59
+ module_function :link_entry
60
+
61
+ private
62
+
63
+ class Entry_ #:nodoc:
64
+ def link(dest)
65
+ case
66
+ when directory?
67
+ if !File.exist?(dest) and descendant_diretory?(dest, path)
68
+ raise ArgumentError, "cannot link directory %s to itself %s" % [path, dest]
69
+ end
70
+ begin
71
+ Dir.mkdir dest
72
+ rescue
73
+ raise unless File.directory?(dest)
74
+ end
75
+ else
76
+ File.link path(), dest
77
+ end
78
+ end
79
+ end
80
+ end
@@ -22,7 +22,7 @@ module Berkshelf
22
22
  type: :boolean,
23
23
  desc: "Freeze the uploaded cookbooks so that they cannot be overwritten.",
24
24
  default: false
25
- desc "upload", "shit"
25
+ desc "upload", "Upload the sources defined in your Berksfile and their dependencies to a Chef Server."
26
26
  def upload
27
27
  begin
28
28
  Chef::Config.from_file(File.expand_path(options[:config]))
@@ -1,3 +1,3 @@
1
1
  module Berkshelf
2
- VERSION = "0.1.5"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -8,20 +8,35 @@ module Berkshelf
8
8
 
9
9
  banner "knife berks install (options)"
10
10
 
11
+ option :shims,
12
+ short: "-s",
13
+ long: "--shims",
14
+ description: "Create a directory of shims pointing to Cookbook Versions.",
15
+ boolean: true
11
16
  option :without,
12
- :short => "-W WITHOUT",
13
- :long => "--without WITHOUT",
14
- :description => "Exclude cookbooks that are in these groups",
15
- :proc => lambda { |w| w.split(",") },
16
- :default => Array.new
17
-
17
+ short: "-W WITHOUT",
18
+ long: "--without WITHOUT",
19
+ description: "Exclude cookbooks that are in these groups",
20
+ proc: lambda { |w| w.split(",") },
21
+ default: Array.new
18
22
  def run
19
23
  ::Berkshelf.ui = ui
24
+ # JW TODO: replace knife with Thor bin. Opt parsing here isn't my favorite.
25
+ if config[:shims]
26
+ config[:shims] = shims_path
27
+ end
28
+
20
29
  cookbook_file = ::Berkshelf::Berksfile.from_file(File.join(Dir.pwd, Berkshelf::DEFAULT_FILENAME))
21
30
  cookbook_file.install(config)
22
31
  rescue BerkshelfError => e
23
32
  Berkshelf.ui.fatal e
24
33
  exit e.status_code
25
34
  end
35
+
36
+ private
37
+
38
+ def shims_path
39
+ File.join(Dir.pwd, "cookbooks")
40
+ end
26
41
  end
27
42
  end
@@ -155,6 +155,44 @@ EOF
155
155
  subject.install
156
156
  end
157
157
  end
158
+
159
+ context "when given a value for :shims pointing to a valid path"do
160
+ let(:cached_one) { double('cached_one', cookbook_name: 'nginx', path: fixtures_path.join("cookbooks", "nginx-0.100.5")) }
161
+ let(:cached_two) { double('cached_two', cookbook_name: 'example_cookbook', path: fixtures_path.join("cookbooks", "example_cookbook-0.5.0")) }
162
+ let(:shims_path) { tmp_path.join("cookbook_shims") }
163
+
164
+ before(:each) do
165
+ resolver.stub(:resolve).and_return([cached_one, cached_two])
166
+ end
167
+
168
+ it "sends a message to write_shims with the given directory and the resolver's solution" do
169
+ subject.should_receive(:write_shims).with(shims_path, [cached_one, cached_two])
170
+ subject.install(shims: shims_path)
171
+ end
172
+ end
173
+ end
174
+
175
+ describe "#write_shims" do
176
+ let(:cached_one) { double('cached_one', cookbook_name: 'nginx', path: fixtures_path.join("cookbooks", "nginx-0.100.5")) }
177
+ let(:cached_two) { double('cached_two', cookbook_name: 'example_cookbook', path: fixtures_path.join("cookbooks", "example_cookbook-0.5.0")) }
178
+ let(:shims_path) { tmp_path.join("cookbook_shims") }
179
+
180
+ before(:each) { subject.write_shims(shims_path, [cached_one, cached_two]) }
181
+
182
+ it "writes a directory at the given path" do
183
+ shims_path.should exist
184
+ shims_path.should be_directory
185
+ end
186
+
187
+ it "writes a symlink of the name of each source within the given directory" do
188
+ linked_path_one = shims_path.join(cached_one.cookbook_name)
189
+ linked_path_two = shims_path.join(cached_two.cookbook_name)
190
+
191
+ linked_path_one.should exist
192
+ linked_path_one.should be_cookbook
193
+ linked_path_two.should exist
194
+ linked_path_two.should be_cookbook
195
+ end
158
196
  end
159
197
  end
160
198
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: berkshelf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2012-06-23 00:00:00.000000000 Z
15
+ date: 2012-06-24 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: dep_selector
@@ -78,6 +78,22 @@ dependencies:
78
78
  - - ~>
79
79
  - !ruby/object:Gem::Version
80
80
  version: 0.15.2
81
+ - !ruby/object:Gem::Dependency
82
+ name: redcarpet
83
+ requirement: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
81
97
  - !ruby/object:Gem::Dependency
82
98
  name: cucumber
83
99
  requirement: !ruby/object:Gem::Requirement
@@ -333,7 +349,7 @@ files:
333
349
  - Gemfile
334
350
  - Guardfile
335
351
  - LICENSE
336
- - README.rdoc
352
+ - README.md
337
353
  - Thorfile
338
354
  - berkshelf.gemspec
339
355
  - features/config.sample.yml
@@ -358,6 +374,7 @@ files:
358
374
  - lib/berkshelf/cookbook_store.rb
359
375
  - lib/berkshelf/core_ext.rb
360
376
  - lib/berkshelf/core_ext/file.rb
377
+ - lib/berkshelf/core_ext/fileutils.rb
361
378
  - lib/berkshelf/core_ext/kernel.rb
362
379
  - lib/berkshelf/core_ext/pathname.rb
363
380
  - lib/berkshelf/downloader.rb
@@ -405,13 +422,13 @@ files:
405
422
  - spec/support/chef_api.rb
406
423
  - spec/support/matchers/file_system_matchers.rb
407
424
  - spec/support/matchers/filepath_matchers.rb
425
+ - spec/unit/berkshelf/berksfile_spec.rb
408
426
  - spec/unit/berkshelf/cached_cookbook_spec.rb
409
427
  - spec/unit/berkshelf/cookbook_source/git_location_spec.rb
410
428
  - spec/unit/berkshelf/cookbook_source/path_location_spec.rb
411
429
  - spec/unit/berkshelf/cookbook_source/site_location_spec.rb
412
430
  - spec/unit/berkshelf/cookbook_source_spec.rb
413
431
  - spec/unit/berkshelf/cookbook_store_spec.rb
414
- - spec/unit/berkshelf/cookbookfile_spec.rb
415
432
  - spec/unit/berkshelf/downloader_spec.rb
416
433
  - spec/unit/berkshelf/dsl_spec.rb
417
434
  - spec/unit/berkshelf/git_spec.rb
@@ -442,7 +459,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
442
459
  version: '0'
443
460
  segments:
444
461
  - 0
445
- hash: 4586386475461844469
462
+ hash: -3927630543327707547
446
463
  requirements: []
447
464
  rubyforge_project:
448
465
  rubygems_version: 1.8.23
@@ -489,13 +506,13 @@ test_files:
489
506
  - spec/support/chef_api.rb
490
507
  - spec/support/matchers/file_system_matchers.rb
491
508
  - spec/support/matchers/filepath_matchers.rb
509
+ - spec/unit/berkshelf/berksfile_spec.rb
492
510
  - spec/unit/berkshelf/cached_cookbook_spec.rb
493
511
  - spec/unit/berkshelf/cookbook_source/git_location_spec.rb
494
512
  - spec/unit/berkshelf/cookbook_source/path_location_spec.rb
495
513
  - spec/unit/berkshelf/cookbook_source/site_location_spec.rb
496
514
  - spec/unit/berkshelf/cookbook_source_spec.rb
497
515
  - spec/unit/berkshelf/cookbook_store_spec.rb
498
- - spec/unit/berkshelf/cookbookfile_spec.rb
499
516
  - spec/unit/berkshelf/downloader_spec.rb
500
517
  - spec/unit/berkshelf/dsl_spec.rb
501
518
  - spec/unit/berkshelf/git_spec.rb
@@ -1,102 +0,0 @@
1
- = Berkshelf
2
-
3
- Manages a Cookbook's, or an Application's, Cookbook dependencies
4
-
5
- == Getting Started
6
-
7
- === Install
8
-
9
- $ gem install berkshelf
10
-
11
- === Use
12
-
13
- ==== Berksfile
14
-
15
- Dependencies are managed via a `Berksfile` in the directory where you want the cookbooks to be installed. The Berksfile, like Bundler's Gemfile, contains which cookbooks are needed and, optionally, where to find them:
16
-
17
- cookbook 'memcached'
18
- cookbook 'nginx'
19
- cookbook 'my_app', path: '/path/to/cookbook'
20
- cookbook 'mysql', git: 'git://github.com/opscode-cookbooks/mysql.git'
21
-
22
- ==== CLI
23
-
24
- The CLI consists of 2 commands: install, update
25
-
26
- $ knife berks (install|update) [(--without|-W) group_to_exclude]
27
-
28
- [install] Installs the from the Berksfile.lock, or Berksfile if the the lockfile does not exist.
29
-
30
- [update] Skips the lockfile and installs fresh
31
-
32
- [init] Prepares a local path to have it's Cookbook dependencies managed by Berkshelf. If the target path is a Cookbook itself, additional Berkshelf support files will be generated to get you started.
33
-
34
- == The Berksfile
35
-
36
- Cookbooks are defined as dependencies by declaring them in the `Berksfile`
37
-
38
- cookbook 'nginx'
39
-
40
- Cookbooks without additional options are assumed to come from the Opscode Community site at the latest available version: http://community.opscode.com/cookbooks
41
-
42
- Options available include:
43
-
44
- version constraint
45
-
46
- cookbook "nginx", "= 0.101.2" # precisely 0.101.2
47
- cookbook "mysql", "< 1.2.4" # less than and not including 1.2.4
48
- cookbook "openssl", "~> 1.0.0" # greater than 1.0.0, and up to but not including 1.1.0
49
-
50
- git
51
-
52
- # ref can be a branch name, tag, or commit hash. If ref is not provided, HEAD is used.
53
- cookbook "mysql", git: "https://github.com/opscode-cookbooks/mysql.git", ref: "<any git ref>"
54
-
55
- path
56
-
57
- # knife berks will look in /path/to/location/of/my_application for the cookbook
58
- cookbook "my_application", path: "/path/to/location/of"
59
-
60
- === Groups
61
-
62
- Groups can be defined via blocks or inline as an option:
63
-
64
- group :solo do
65
- cookbook 'base'
66
- end
67
-
68
- cookbook 'base', :group => 'solo'
69
-
70
- When using install or update, groups can be excluded with the --without GROUP_NAME or -W GROUP_NAME flags.
71
-
72
- = Contributing
73
-
74
- == Running tests
75
-
76
- === Install prerequisites
77
-
78
- Install the latest version of {Bundler}[http://gembundler.com]
79
-
80
- $ gem install bundler
81
-
82
- Clone the project
83
-
84
- $ git clone git://github.com/RiotGames/berkshelf.git
85
-
86
- and run:
87
-
88
- $ cd berkshelf
89
- $ bundle install
90
-
91
- Bundler will install all gems and their dependencies required for testing and developing.
92
-
93
- === Running unit (RSpec) and acceptance (Cucumber) tests
94
-
95
- $ bundle exec guard start
96
-
97
- = Authors and Contributors
98
-
99
- * Josiah Kiehl (<josiah@skirmisher.net>)
100
- * Jamie Winsor (<jamie@vialstudios.com>)
101
- * Erik Hollensbe (<erik@hollensbe.org>)
102
- * Michael Ivey (<ivey@gweezlebur.com>)