berkshelf 2.0.1 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,14 @@
1
+ # 2.0.3
2
+
3
+ * Fix issue where groups defined in the Berksfile would not be evaluated
4
+
5
+ # 2.0.2
6
+
7
+ * Fix issue with shellout on Windows. This would effect uploads and downloads of cookbooks using the Git location.
8
+ * The Berksfile DSL now evaluates in a clean room to prevent end-users from calling restricted methods.
9
+ * Fix issue with `berks upload -D` not properly skipping dependencies
10
+ * Added friendly error message when an unknown license is chosen during cookbook generation
11
+
1
12
  # 2.0.1
2
13
 
3
14
  * Improve performance of `berks upload`. It will now properly respect the Lockfile
@@ -38,7 +38,7 @@ Gem::Specification.new do |s|
38
38
  s.add_dependency 'hashie', '>= 2.0.2'
39
39
  s.add_dependency 'minitar', '~> 0.5.4'
40
40
  s.add_dependency 'retryable', '~> 1.3.3'
41
- s.add_dependency 'ridley', '~> 1.0.0'
41
+ s.add_dependency 'ridley', '~> 1.0.2'
42
42
  s.add_dependency 'solve', '>= 0.4.4'
43
43
  s.add_dependency 'test-kitchen', '>= 1.0.0.alpha7'
44
44
  s.add_dependency 'thor', '~> 0.18.0'
@@ -263,3 +263,34 @@ Feature: Uploading cookbooks to a Chef Server
263
263
  The cookbook 'cookbook with spaces' has invalid filenames:
264
264
  """
265
265
  And the CLI should exit with the status code for error "InvalidCookbookFiles"
266
+
267
+ @chef_server @slow_process
268
+ Scenario: With the --skip-dependencies flag
269
+ Given the cookbook store has the cookbooks:
270
+ | fake | 1.0.0 |
271
+ | ekaf | 2.0.0 |
272
+ And the cookbook store contains a cookbook "reset" "3.4.5" with dependencies:
273
+ | fake | ~> 1.0.0 |
274
+ And I write to "Berksfile" with:
275
+ """
276
+ cookbook 'fake', '1.0.0'
277
+ cookbook 'ekaf', '2.0.0'
278
+ cookbook 'reset', '3.4.5'
279
+ """
280
+ And the Chef server does not have the cookbooks:
281
+ | fake | 1.0.0 |
282
+ | ekaf | 2.0.0 |
283
+ | reset | 3.4.5 |
284
+ When I successfully run `berks upload reset -D`
285
+ Then the output should contain:
286
+ """
287
+ Uploading reset (3.4.5) to: 'http://localhost:4000/'
288
+ Uploading fake (1.0.0) to: 'http://localhost:4000/'
289
+ """
290
+ And the Chef server should have the cookbooks:
291
+ | reset | 3.4.5 |
292
+ | fake | 1.0.0 |
293
+ And the Chef server should not have the cookbooks:
294
+ | ekaf | 2.0.0 |
295
+ And the exit status should be 0
296
+
@@ -14,9 +14,10 @@ require 'tmpdir'
14
14
  require 'uri'
15
15
  require 'zlib'
16
16
 
17
- require 'berkshelf/core_ext'
18
- require 'berkshelf/errors'
19
- require 'thor/monkies'
17
+ require_relative 'berkshelf/core_ext'
18
+ require_relative 'berkshelf/errors'
19
+ require_relative 'berkshelf/mixin'
20
+ require_relative 'berkshelf/thor_ext'
20
21
 
21
22
  JSON.create_id = nil
22
23
 
@@ -24,7 +25,6 @@ module Berkshelf
24
25
  DEFAULT_FILENAME = 'Berksfile'.freeze
25
26
 
26
27
  class << self
27
- require 'berkshelf/mixin/logging'
28
28
  include Berkshelf::Mixin::Logging
29
29
 
30
30
  attr_accessor :ui
@@ -160,4 +160,4 @@ require_relative 'berkshelf/ui'
160
160
  require_relative 'berkshelf/version'
161
161
 
162
162
  Ridley.logger = Celluloid.logger = Berkshelf.logger = Logger.new(STDOUT)
163
- Berkshelf.logger.level = Logger::WARN
163
+ Berkshelf.logger.level = Logger::WARN
@@ -1,21 +1,16 @@
1
1
  module Berkshelf
2
2
  class Berksfile
3
- require 'berkshelf/mixin/logging'
4
- include Berkshelf::Mixin::Logging
5
-
6
- extend Forwardable
7
-
8
3
  class << self
9
- # @param [String] file
4
+ # @param [#to_s] file
10
5
  # a path on disk to a Berksfile to instantiate from
11
6
  #
12
7
  # @return [Berksfile]
13
8
  def from_file(file)
14
- content = File.read(file)
15
- object = new(file)
16
- object.load(content)
17
- rescue Errno::ENOENT => e
9
+ new(file).dsl_eval_file(file)
10
+ rescue Errno::ENOENT => ex
18
11
  raise BerksfileNotFound, "No Berksfile or Berksfile.lock found at: #{file}"
12
+ rescue => ex
13
+ raise BerksfileReadError.new(ex)
19
14
  end
20
15
 
21
16
  # Copy all cached_cookbooks to the given directory. Each cookbook will be contained in
@@ -60,6 +55,16 @@ module Berkshelf
60
55
  end
61
56
  end
62
57
 
58
+ include Berkshelf::Mixin::Logging
59
+ include Berkshelf::Mixin::DSLEval
60
+ extend Forwardable
61
+
62
+ expose_method :metadata
63
+ expose_method :group
64
+ expose_method :site
65
+ expose_method :chef_api
66
+ expose_method :cookbook
67
+
63
68
  @@active_group = nil
64
69
 
65
70
  # @return [String]
@@ -549,7 +554,7 @@ module Berkshelf
549
554
  end
550
555
 
551
556
  if options[:skip_dependencies]
552
- missing_cookbooks = options.fetch(:cookbooks, nil) - solution.map(&:cookbook_name)
557
+ missing_cookbooks = options.fetch(:cookbooks, nil) - cached_cookbooks.map(&:cookbook_name)
553
558
  unless missing_cookbooks.empty?
554
559
  msg = "Unable to upload cookbooks: #{missing_cookbooks.sort.join(', ')}\n"
555
560
  msg << "Specified cookbooks must be defined within the Berkshelf file when using the"
@@ -681,23 +686,6 @@ module Berkshelf
681
686
  { solution: resolver.resolve, sources: resolver.sources }
682
687
  end
683
688
 
684
- # Reload this instance of Berksfile with the given content. The content
685
- # is a string that may contain terms from the included DSL.
686
- #
687
- # @param [String] content
688
- #
689
- # @raise [BerksfileReadError] if Berksfile contains bad content
690
- #
691
- # @return [Berksfile]
692
- def load(content)
693
- begin
694
- instance_eval(content)
695
- rescue => e
696
- raise BerksfileReadError.new(e)
697
- end
698
- self
699
- end
700
-
701
689
  # Get the lockfile corresponding to this Berksfile. This is necessary because
702
690
  # the user can specify a different path to the Berksfile. So assuming the lockfile
703
691
  # is named "Berksfile.lock" is a poor assumption.
@@ -2,6 +2,14 @@ module Berkshelf
2
2
  class CookbookGenerator < BaseGenerator
3
3
  require_relative 'config'
4
4
 
5
+ LICENSES = [
6
+ "apachev2",
7
+ "gplv2",
8
+ "gplv3",
9
+ "mit",
10
+ "reserved"
11
+ ].freeze
12
+
5
13
  argument :name,
6
14
  type: :string,
7
15
  required: true
@@ -79,7 +87,7 @@ module Berkshelf
79
87
  when 'mit'; 'MIT'
80
88
  when 'reserved'; 'All rights reserved'
81
89
  else
82
- raise Berkshelf::InternalError, "Unknown license: '#{options[:license]}'"
90
+ raise Berkshelf::LicenseNotFound.new(options[:license])
83
91
  end
84
92
  end
85
93
 
@@ -95,7 +103,7 @@ module Berkshelf
95
103
  when 'mit'; 'licenses/mit.erb'
96
104
  when 'reserved'; 'licenses/reserved.erb'
97
105
  else
98
- raise Berkshelf::InternalError, "Unknown license: '#{options[:license]}'"
106
+ raise Berkshelf::LicenseNotFound.new(options[:license])
99
107
  end
100
108
  end
101
109
 
@@ -185,7 +185,6 @@ module Berkshelf
185
185
 
186
186
  class ConfigExists < BerkshelfError; status_code(116); end
187
187
  class ConfigurationError < BerkshelfError; status_code(117); end
188
- class CommandUnsuccessful < BerkshelfError; status_code(118); end
189
188
  class InsufficientPrivledges < BerkshelfError; status_code(119); end
190
189
  class ExplicitCookbookNotFound < BerkshelfError; status_code(120); end
191
190
  class ValidationFailed < BerkshelfError; status_code(121); end
@@ -340,10 +339,25 @@ module Berkshelf
340
339
  end
341
340
  end
342
341
 
342
+ class LicenseNotFound < BerkshelfError
343
+ status_code(134)
344
+
345
+ attr_reader :license
346
+
347
+ def initialize(license)
348
+ @license = license
349
+ end
350
+
351
+ def to_s
352
+ "Unknown license: '#{license}'\n" +
353
+ "Available licenses: #{Berkshelf::CookbookGenerator::LICENSES.join(', ')}"
354
+ end
355
+ end
356
+
343
357
  # Raised when a cookbook or its recipes contain a space or invalid
344
358
  # character in the path.
345
359
  class ConfigNotFound < BerkshelfError
346
- status_code(133)
360
+ status_code(135)
347
361
 
348
362
  # @param [String] type
349
363
  # the type of config that was not found (Berkshelf, Chef, etc)
@@ -2,9 +2,6 @@ require 'uri'
2
2
 
3
3
  module Berkshelf
4
4
  class Git
5
- require 'berkshelf/mixin/shellout'
6
- extend Berkshelf::Mixin::Shellout
7
-
8
5
  GIT_REGEXP = URI.regexp(%w(http https ssh git+ssh git rsync))
9
6
  SCP_REGEXP = /^(.+@)?[\w\d\.-]+:.*$/
10
7
 
@@ -12,6 +9,8 @@ module Berkshelf
12
9
  HAS_SPACE_RE = %r{\s}.freeze
13
10
 
14
11
  class << self
12
+ include Ridley::Mixin::ShellOut
13
+
15
14
  # @overload git(commands)
16
15
  # Shellout to the Git executable on your system with the given commands.
17
16
  #
@@ -22,13 +21,13 @@ module Berkshelf
22
21
  def git(*command)
23
22
  command.unshift(git_cmd)
24
23
  command_str = command.map { |p| quote_cmd_arg(p) }.join(' ')
25
- result = shellout(command_str)
24
+ response = shell_out(command_str)
26
25
 
27
- unless result.success?
28
- raise GitError.new(result.stderr)
26
+ unless response.success?
27
+ raise GitError.new(response.stderr.strip)
29
28
  end
30
29
 
31
- result.stdout
30
+ response.stdout.strip
32
31
  end
33
32
 
34
33
  # Clone a remote Git repository to disk
@@ -0,0 +1,7 @@
1
+ module Berkshelf
2
+ module Mixin; end
3
+ end
4
+
5
+ Dir["#{File.dirname(__FILE__)}/mixin/*.rb"].sort.each do |path|
6
+ require_relative "mixin/#{File.basename(path, '.rb')}"
7
+ end
@@ -0,0 +1,58 @@
1
+ module Berkshelf
2
+ module Mixin
3
+ module DSLEval
4
+ class CleanRoom
5
+ attr_reader :instance
6
+
7
+ def initialize(instance)
8
+ @instance = instance
9
+ end
10
+ end
11
+
12
+ class << self
13
+ def included(base)
14
+ base.send(:extend, ClassMethods)
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ def clean_room
20
+ @clean_room ||= begin
21
+ exposed_methods = self.exposed_methods
22
+
23
+ Class.new(DSLEval::CleanRoom) do
24
+ exposed_methods.each do |exposed_method|
25
+ define_method(exposed_method) do |*args, &block|
26
+ instance.send(exposed_method, *args, &block)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ def expose_method(method)
34
+ exposed_methods << method.to_sym
35
+ end
36
+
37
+ def exposed_methods
38
+ @exposed_methods ||= Array.new
39
+ end
40
+ end
41
+
42
+ # @return [Object]
43
+ def dsl_eval(&block)
44
+ self.class.clean_room.new(self).instance_eval(&block)
45
+ self
46
+ end
47
+
48
+ # @param [String] filepath
49
+ #
50
+ # @return [Object]
51
+ def dsl_eval_file(filepath)
52
+ filepath = filepath.to_s
53
+ contents = File.read(filepath)
54
+ dsl_eval { eval(contents, binding, filepath, 1) }
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/thor_ext/*.rb"].sort.each do |path|
2
+ require_relative "thor_ext/#{File.basename(path, '.rb')}"
3
+ end
@@ -1,3 +1,3 @@
1
1
  module Berkshelf
2
- VERSION = '2.0.1'
2
+ VERSION = "2.0.3"
3
3
  end
@@ -2,10 +2,27 @@ require 'spec_helper'
2
2
 
3
3
  describe Berkshelf::Berksfile do
4
4
  describe '.from_file' do
5
- let(:cookbook_file) { fixtures_path.join('lockfile_spec', 'with_lock', 'Berksfile') }
5
+ let(:content) do
6
+ <<-EOF.strip
7
+ cookbook 'ntp', '<= 1.0.0'
8
+ cookbook 'mysql'
9
+ cookbook 'nginx', '< 0.101.2'
10
+ cookbook 'ssh_known_hosts2', :git => 'https://github.com/erikh/chef-ssh_known_hosts2.git'
11
+ EOF
12
+ end
13
+ let(:berksfile) { tmp_path.join('Berksfile') }
6
14
 
7
- it 'reads a Berksfile and returns an instance Berksfile' do
8
- expect(Berkshelf::Berksfile.from_file(cookbook_file)).to be_a(Berkshelf::Berksfile)
15
+ before { File.open(berksfile, 'w+') { |f| f.write(content) } }
16
+ subject(:from_file) { described_class.from_file(berksfile) }
17
+
18
+ it "reads the content of the Berksfile and binds them to a new instance" do
19
+ %w(ntp mysql nginx ssh_known_hosts2).each do |name|
20
+ expect(subject).to have_source(name)
21
+ end
22
+ end
23
+
24
+ it "returns an instance of Berkshelf::Berksfile" do
25
+ expect(subject).to be_a(described_class)
9
26
  end
10
27
 
11
28
  context 'when Berksfile does not exist at given path' do
@@ -340,29 +357,6 @@ describe Berkshelf::Berksfile do
340
357
  end
341
358
  end
342
359
 
343
- describe '#load' do
344
- let(:content) do
345
- <<-EOF.strip
346
- cookbook 'ntp', '<= 1.0.0'
347
- cookbook 'mysql'
348
- cookbook 'nginx', '< 0.101.2'
349
- cookbook 'ssh_known_hosts2', :git => 'https://github.com/erikh/chef-ssh_known_hosts2.git'
350
- EOF
351
- end
352
-
353
- it 'reads the content of a Berksfile and adds the sources to the Shelf' do
354
- subject.load(content)
355
-
356
- %w(ntp mysql nginx ssh_known_hosts2).each do |name|
357
- expect(subject).to have_source(name)
358
- end
359
- end
360
-
361
- it 'returns an instance of Berksfile' do
362
- expect(subject.load(content)).to be_a(Berkshelf::Berksfile)
363
- end
364
- end
365
-
366
360
  describe '#add_source' do
367
361
  let(:name) { 'cookbook_one' }
368
362
  let(:constraint) { '= 1.2.0' }
@@ -77,4 +77,14 @@ describe Berkshelf::CookbookGenerator do
77
77
  }
78
78
  end
79
79
  end
80
+
81
+ context "given an invalid option for :license" do
82
+ subject(:run) do
83
+ capture(:stdout) { described_class.new([target, name], license: 'not-there').invoke_all }
84
+ end
85
+
86
+ it "raises a LicenseNotFound error" do
87
+ expect { run }.to raise_error(Berkshelf::LicenseNotFound)
88
+ end
89
+ end
80
90
  end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Berkshelf::Mixin::DSLEval do
4
+ let(:klass) do
5
+ klass = Class.new
6
+ klass.send(:include, described_class)
7
+ klass
8
+ end
9
+
10
+ describe "ClassMethods" do
11
+ describe "::clean_room" do
12
+ subject { klass.clean_room }
13
+
14
+ it "returns an anonymous class inheriting from DSLEval::CleanRoom" do
15
+ expect(subject.superclass).to eql(described_class::CleanRoom)
16
+ end
17
+ end
18
+
19
+ describe "::expose_method" do
20
+ subject { klass }
21
+
22
+ it "adds a method to the exposed methods" do
23
+ klass.expose_method(:something)
24
+ expect(subject.exposed_methods).to have(1).item
25
+ end
26
+ end
27
+
28
+ describe "::exposed_methods" do
29
+ it "returns an array" do
30
+ expect(klass.exposed_methods).to be_a(Array)
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "#dsl_eval" do
36
+ subject do
37
+ klass.new.dsl_eval { }
38
+ end
39
+
40
+ it "returns an instance of the including class" do
41
+ expect(subject).to be_a(klass)
42
+ end
43
+ end
44
+
45
+ describe "#dsl_eval_file" do
46
+ let(:filepath) { tmp_path.join('somefile') }
47
+ before { FileUtils.touch(filepath) }
48
+
49
+ subject { klass.new.dsl_eval_file(filepath) }
50
+
51
+ it "returns an instance of the including class" do
52
+ expect(subject).to be_a(klass)
53
+ end
54
+ end
55
+ 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: 2.0.1
4
+ version: 2.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2013-06-07 00:00:00.000000000 Z
16
+ date: 2013-06-11 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: activesupport
@@ -150,7 +150,7 @@ dependencies:
150
150
  requirements:
151
151
  - - ~>
152
152
  - !ruby/object:Gem::Version
153
- version: 1.0.0
153
+ version: 1.0.2
154
154
  type: :runtime
155
155
  prerelease: false
156
156
  version_requirements: !ruby/object:Gem::Requirement
@@ -158,7 +158,7 @@ dependencies:
158
158
  requirements:
159
159
  - - ~>
160
160
  - !ruby/object:Gem::Version
161
- version: 1.0.0
161
+ version: 1.0.2
162
162
  - !ruby/object:Gem::Dependency
163
163
  name: solve
164
164
  requirement: !ruby/object:Gem::Requirement
@@ -579,7 +579,6 @@ files:
579
579
  - lib/berkshelf/chef/cookbook.rb
580
580
  - lib/berkshelf/chef/cookbook/chefignore.rb
581
581
  - lib/berkshelf/cli.rb
582
- - lib/berkshelf/command.rb
583
582
  - lib/berkshelf/commands/shelf.rb
584
583
  - lib/berkshelf/commands/test_command.rb
585
584
  - lib/berkshelf/community_rest.rb
@@ -608,17 +607,18 @@ files:
608
607
  - lib/berkshelf/locations/site_location.rb
609
608
  - lib/berkshelf/lockfile.rb
610
609
  - lib/berkshelf/logger.rb
610
+ - lib/berkshelf/mixin.rb
611
611
  - lib/berkshelf/mixin/config.rb
612
+ - lib/berkshelf/mixin/dsl_eval.rb
612
613
  - lib/berkshelf/mixin/logging.rb
613
- - lib/berkshelf/mixin/shellout.rb
614
614
  - lib/berkshelf/resolver.rb
615
615
  - lib/berkshelf/test.rb
616
616
  - lib/berkshelf/thor.rb
617
+ - lib/berkshelf/thor_ext.rb
618
+ - lib/berkshelf/thor_ext/hash_with_indifferent_access.rb
619
+ - lib/berkshelf/thor_ext/shell.rb
617
620
  - lib/berkshelf/ui.rb
618
621
  - lib/berkshelf/version.rb
619
- - lib/thor/monkies.rb
620
- - lib/thor/monkies/hash_with_indifferent_access.rb
621
- - lib/thor/monkies/shell.rb
622
622
  - spec/config/berkshelf.pem
623
623
  - spec/config/knife.rb
624
624
  - spec/config/validator.pem
@@ -693,6 +693,7 @@ files:
693
693
  - spec/unit/berkshelf/locations/site_location_spec.rb
694
694
  - spec/unit/berkshelf/lockfile_spec.rb
695
695
  - spec/unit/berkshelf/logger_spec.rb
696
+ - spec/unit/berkshelf/mixin/dsl_eval_spec.rb
696
697
  - spec/unit/berkshelf/mixin/logging_spec.rb
697
698
  - spec/unit/berkshelf/resolver_spec.rb
698
699
  - spec/unit/berkshelf/ui_spec.rb
@@ -830,6 +831,7 @@ test_files:
830
831
  - spec/unit/berkshelf/locations/site_location_spec.rb
831
832
  - spec/unit/berkshelf/lockfile_spec.rb
832
833
  - spec/unit/berkshelf/logger_spec.rb
834
+ - spec/unit/berkshelf/mixin/dsl_eval_spec.rb
833
835
  - spec/unit/berkshelf/mixin/logging_spec.rb
834
836
  - spec/unit/berkshelf/resolver_spec.rb
835
837
  - spec/unit/berkshelf/ui_spec.rb
@@ -1,116 +0,0 @@
1
- module Berkshelf
2
- # This superclass is responsible for handling common command methods and options.
3
- module Command
4
- # Initialize a new instance of the parent class
5
- #
6
- # @param [Hash] options
7
- # the list of options to pass to the installer
8
- def initialize(options = {})
9
- @options = options
10
- end
11
-
12
- private
13
-
14
- # Validate the options hash, ensuring there are no conflicting arguments
15
- #
16
- # @raise [Berkshelf::ArgumentError]
17
- # if there are conflicting or invalid options
18
- def validate_options!
19
- if options[:except] && options[:only]
20
- raise Berkshelf::ArgumentError, 'Cannot specify both :except and :only'
21
- end
22
-
23
- if options[:cookbooks] && (options[:except] || options[:only])
24
- Berkshelf.ui.warn ':cookbooks were supplied to update(), so :except and :only are ignored...'
25
- end
26
- end
27
-
28
- # Ensure the berkshelf directory is created and accessible.
29
- def ensure_berkshelf_directory!
30
- unless File.exists?(Berkshelf.berkshelf_path)
31
- FileUtils.mkdir_p(Berkshelf.berkshelf_path)
32
- end
33
- end
34
-
35
- # Check for the presence of a Berksfile. Berkshelf cannot do anything
36
- # without the presence of a Berksfile.lock.
37
- def ensure_berksfile!
38
- unless File.exists?(options[:berksfile])
39
- raise Berkshelf::BerksfileNotFound, "No #{options[:berksfile]} was found at ."
40
- end
41
- end
42
-
43
- # Check that the Berksfile has content. If the Berksfile is empty, raise
44
- # an exception to require at least one definition.
45
- def ensure_berksfile_content!
46
- begin
47
- unless File.read(options[:berksfile]).size > 1
48
- raise Berkshelf::BerksfileNotFound, "Your #{options[:berksfile]} is empty! You need at least one cookbook definition."
49
- end
50
- rescue Errno::ENOENT
51
- ensure_berksfile!
52
- end
53
- end
54
-
55
- # @return [Hash]
56
- # the options for this installer
57
- def options
58
- @options ||= {}
59
- end
60
-
61
- # Attempt to load and parse the lockfile associated with this berksfile.
62
- #
63
- # @return [Berkshelf::Lockfile, nil]
64
- # the lockfile for the current berksfile
65
- def lockfile
66
- @lockfile ||= berksfile.lockfile
67
- end
68
-
69
- # Load the Berksfile for the current project.
70
- #
71
- # @raise [Berkshelf::BerksfileNotFound]
72
- # if the file is not found
73
- #
74
- # @return [Berkshelf::Berksfile]
75
- # the current Berksfile
76
- def berksfile
77
- @berksfile ||= Berkshelf::Berksfile.from_file(options[:berksfile])
78
- end
79
-
80
- # Filter the list of sources from the options passed to the installer.
81
- #
82
- # @param [Array<Berkshelf::CookbookSource>] sources
83
- # the list of sources to resolve
84
- #
85
- # @raise [Berkshelf::ArgumentError]
86
- # if a value for both :except and :only is provided
87
- #
88
- # @raise [Berkshelf::CookbookNotFound]
89
- # if a cookbook name is specified that does not exist
90
- #
91
- # @return [Array<Berkshelf::CookbookSource>]
92
- def filter(sources)
93
- cookbooks = Array(options[:cookbooks]).map(&:to_s)
94
- except = Array(options[:except]).map(&:to_sym)
95
- only = Array(options[:only]).map(&:to_sym)
96
-
97
- case
98
- when !cookbooks.empty?
99
- missing_cookbooks = (cookbooks - sources.map(&:name))
100
-
101
- unless missing_cookbooks.empty?
102
- raise Berkshelf::CookbookNotFound, "Could not find cookbooks #{missing_cookbooks.collect{|cookbook| "'#{cookbook}'"}.join(', ')} in any of the sources. #{missing_cookbooks.size == 1 ? 'Is it' : 'Are they' } in your Berksfile?"
103
- end
104
-
105
- sources.select { |source| options[:cookbooks].include?(source.name) }
106
- when !except.empty?
107
- sources.select { |source| (except & source.groups).empty? }
108
- when !only.empty?
109
- sources.select { |source| !(only & source.groups).empty? }
110
- else
111
- sources
112
- end
113
- end
114
-
115
- end
116
- end
@@ -1,71 +0,0 @@
1
- require 'hashie'
2
-
3
- module Berkshelf
4
- module Mixin
5
- module Shellout
6
- # Perform a cross-platform shell command, returning the process result.
7
- # This uses Process.spawn under the hood for Ruby 1.9, so see
8
- # {Process.spawn} for more information.
9
- #
10
- # On JRuby, a system command is used and $stdout and $stderr are captured.
11
- #
12
- # @param [String] command
13
- # @param [Hash] options
14
- # a list of options to send to {Process.spawn}
15
- # @return [Hashie::Mash]
16
- # information about the command including:
17
- # - stderr
18
- # - stdout
19
- # - exitstatus
20
- # - pid
21
- # - success?
22
- # - failure?
23
- def shellout(command)
24
- if defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java' # jruby
25
- out, err, e = capture do
26
- system(command)
27
- end
28
- else
29
- begin
30
- out_file, err_file = Tempfile.new('berkshelf.stdout'), Tempfile.new('berkshelf.stderr')
31
- pid = Process.spawn({}, command, out: out_file.to_i, err: err_file.to_i)
32
- Process.waitpid(pid)
33
-
34
- out, err = File.read(out_file), File.read(err_file)
35
- e = $?
36
- rescue Errno::ENOENT
37
- out, err = "", "command not found: #{command}"
38
- e = $?
39
- end
40
- end
41
-
42
- Hashie::Mash.new({
43
- stdout: out.strip,
44
- stderr: err.strip,
45
- exitstatus: e.exitstatus,
46
- pid: e.pid,
47
- success?: e.success?,
48
- failure?: !e.success?
49
- })
50
- end
51
-
52
- private
53
- # Execute the given block, capturing $stdout, $stderr, and the returned process.
54
- #
55
- # @return [Array<StringIO, StringIO, Process>]
56
- # a tuple of $stdout, $stderr, and the Process
57
- def capture(&block)
58
- out, err = StringIO.new, StringIO.new
59
- $stdout, $stderr = out, err
60
-
61
- yield
62
-
63
- out.rewind
64
- err.rewind
65
- return out.read, err.read, $?
66
- ensure
67
- $stdout, $stderr = STDOUT, STDERR
68
- end
69
- end
70
- end
71
- end
@@ -1,3 +0,0 @@
1
- Dir["#{File.dirname(__FILE__)}/monkies/*.rb"].sort.each do |path|
2
- require "thor/monkies/#{File.basename(path, '.rb')}"
3
- end