berkshelf 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/.gitignore +20 -0
  2. data/.rbenv-version +1 -0
  3. data/Gemfile +3 -0
  4. data/Guardfile +23 -0
  5. data/LICENSE +22 -0
  6. data/README.rdoc +102 -0
  7. data/Thorfile +47 -0
  8. data/berkshelf.gemspec +39 -0
  9. data/features/config.sample.yml +3 -0
  10. data/features/init_command.feature +40 -0
  11. data/features/install.feature +55 -0
  12. data/features/lockfile.feature +22 -0
  13. data/features/step_definitions/chef_server_steps.rb +13 -0
  14. data/features/step_definitions/cli_steps.rb +56 -0
  15. data/features/step_definitions/filesystem_steps.rb +79 -0
  16. data/features/support/env.rb +55 -0
  17. data/features/update.feature +23 -0
  18. data/features/upload_command.feature +43 -0
  19. data/features/without.feature +25 -0
  20. data/lib/berkshelf.rb +90 -0
  21. data/lib/berkshelf/berksfile.rb +170 -0
  22. data/lib/berkshelf/cached_cookbook.rb +253 -0
  23. data/lib/berkshelf/cookbook_source.rb +139 -0
  24. data/lib/berkshelf/cookbook_source/git_location.rb +54 -0
  25. data/lib/berkshelf/cookbook_source/path_location.rb +27 -0
  26. data/lib/berkshelf/cookbook_source/site_location.rb +206 -0
  27. data/lib/berkshelf/cookbook_store.rb +77 -0
  28. data/lib/berkshelf/core_ext.rb +3 -0
  29. data/lib/berkshelf/core_ext/file.rb +14 -0
  30. data/lib/berkshelf/core_ext/kernel.rb +33 -0
  31. data/lib/berkshelf/core_ext/pathname.rb +24 -0
  32. data/lib/berkshelf/downloader.rb +92 -0
  33. data/lib/berkshelf/dsl.rb +39 -0
  34. data/lib/berkshelf/errors.rb +20 -0
  35. data/lib/berkshelf/generator_files/Berksfile.erb +3 -0
  36. data/lib/berkshelf/generator_files/chefignore +44 -0
  37. data/lib/berkshelf/git.rb +70 -0
  38. data/lib/berkshelf/init_generator.rb +38 -0
  39. data/lib/berkshelf/lockfile.rb +42 -0
  40. data/lib/berkshelf/resolver.rb +176 -0
  41. data/lib/berkshelf/tx_result.rb +12 -0
  42. data/lib/berkshelf/tx_result_set.rb +37 -0
  43. data/lib/berkshelf/uploader.rb +153 -0
  44. data/lib/berkshelf/version.rb +3 -0
  45. data/lib/chef/knife/berks_init.rb +29 -0
  46. data/lib/chef/knife/berks_install.rb +27 -0
  47. data/lib/chef/knife/berks_update.rb +23 -0
  48. data/lib/chef/knife/berks_upload.rb +39 -0
  49. data/spec/fixtures/Berksfile +3 -0
  50. data/spec/fixtures/cookbooks/example_cookbook-0.5.0/README.md +12 -0
  51. data/spec/fixtures/cookbooks/example_cookbook-0.5.0/metadata.rb +6 -0
  52. data/spec/fixtures/cookbooks/example_cookbook-0.5.0/recipes/default.rb +8 -0
  53. data/spec/fixtures/cookbooks/example_cookbook/README.md +12 -0
  54. data/spec/fixtures/cookbooks/example_cookbook/metadata.rb +6 -0
  55. data/spec/fixtures/cookbooks/example_cookbook/recipes/default.rb +8 -0
  56. data/spec/fixtures/cookbooks/invalid_ruby_files-1.0.0/recipes/default.rb +1 -0
  57. data/spec/fixtures/cookbooks/invalid_template_files-1.0.0/templates/default/broken.erb +1 -0
  58. data/spec/fixtures/cookbooks/nginx-0.100.5/README.md +77 -0
  59. data/spec/fixtures/cookbooks/nginx-0.100.5/attributes/default.rb +65 -0
  60. data/spec/fixtures/cookbooks/nginx-0.100.5/definitions/nginx_site.rb +35 -0
  61. data/spec/fixtures/cookbooks/nginx-0.100.5/files/default/mime.types +73 -0
  62. data/spec/fixtures/cookbooks/nginx-0.100.5/files/ubuntu/mime.types +73 -0
  63. data/spec/fixtures/cookbooks/nginx-0.100.5/libraries/nginxlib.rb +1 -0
  64. data/spec/fixtures/cookbooks/nginx-0.100.5/metadata.rb +91 -0
  65. data/spec/fixtures/cookbooks/nginx-0.100.5/providers/defprovider.rb +1 -0
  66. data/spec/fixtures/cookbooks/nginx-0.100.5/recipes/default.rb +59 -0
  67. data/spec/fixtures/cookbooks/nginx-0.100.5/resources/defresource.rb +1 -0
  68. data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/nginx.pill.erb +15 -0
  69. data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/plugins/nginx.rb.erb +66 -0
  70. data/spec/fixtures/lockfile_spec/with_lock/Berksfile +1 -0
  71. data/spec/fixtures/lockfile_spec/without_lock/Berksfile.lock +5 -0
  72. data/spec/spec_helper.rb +92 -0
  73. data/spec/support/chef_api.rb +27 -0
  74. data/spec/support/matchers/file_system_matchers.rb +115 -0
  75. data/spec/support/matchers/filepath_matchers.rb +19 -0
  76. data/spec/unit/berkshelf/cached_cookbook_spec.rb +420 -0
  77. data/spec/unit/berkshelf/cookbook_source/git_location_spec.rb +59 -0
  78. data/spec/unit/berkshelf/cookbook_source/path_location_spec.rb +34 -0
  79. data/spec/unit/berkshelf/cookbook_source/site_location_spec.rb +166 -0
  80. data/spec/unit/berkshelf/cookbook_source_spec.rb +194 -0
  81. data/spec/unit/berkshelf/cookbook_store_spec.rb +71 -0
  82. data/spec/unit/berkshelf/cookbookfile_spec.rb +160 -0
  83. data/spec/unit/berkshelf/downloader_spec.rb +82 -0
  84. data/spec/unit/berkshelf/dsl_spec.rb +42 -0
  85. data/spec/unit/berkshelf/git_spec.rb +63 -0
  86. data/spec/unit/berkshelf/init_generator_spec.rb +52 -0
  87. data/spec/unit/berkshelf/lockfile_spec.rb +25 -0
  88. data/spec/unit/berkshelf/resolver_spec.rb +126 -0
  89. data/spec/unit/berkshelf/tx_result_set_spec.rb +77 -0
  90. data/spec/unit/berkshelf/tx_result_spec.rb +21 -0
  91. data/spec/unit/berkshelf/uploader_spec.rb +71 -0
  92. data/spec/unit/berkshelf_spec.rb +29 -0
  93. metadata +411 -0
@@ -0,0 +1,79 @@
1
+ require 'aruba/api'
2
+
3
+ World(Aruba::Api)
4
+
5
+ Given /^a cookbook named "(.*?)"$/ do |name|
6
+ steps %{
7
+ Given a directory named "#{name}"
8
+ And an empty file named "#{name}/metadata.rb"
9
+ }
10
+ end
11
+
12
+ Given /^I do not have a Berksfile$/ do
13
+ in_current_dir { FileUtils.rm_f(Berkshelf::DEFAULT_FILENAME) }
14
+ end
15
+
16
+ Given /^I do not have a Berksfile\.lock$/ do
17
+ in_current_dir { FileUtils.rm_f(Berkshelf::Lockfile::DEFAULT_FILENAME) }
18
+ end
19
+
20
+ Given /^the cookbook "(.*?)" has the file "(.*?)" with:$/ do |cookbook_name, file_name, content|
21
+ write_file(File.join(cookbook_name, file_name), content)
22
+ end
23
+
24
+ Then /^the cookbook store should have the cookbooks:$/ do |cookbooks|
25
+ cookbooks.raw.each do |name, version|
26
+ cookbook_store.should have_structure {
27
+ directory "#{name}-#{version}" do
28
+ file "metadata.rb" do
29
+ contains version
30
+ end
31
+ end
32
+ }
33
+ end
34
+ end
35
+
36
+ Then /^the cookbook store should not have the cookbooks:$/ do |cookbooks|
37
+ cookbooks.raw.each do |name, version|
38
+ cookbook_store.should_not have_structure {
39
+ directory "#{name}-#{version}"
40
+ }
41
+ end
42
+ end
43
+
44
+ Then /^the cookbook "(.*?)" should have the following files:$/ do |name, files|
45
+ check_file_presence(files.raw.map{|file_row| File.join(name, file_row[0])}, true)
46
+ end
47
+
48
+ Then /^the file "(.*?)" in the cookbook "(.*?)" should contain:$/ do |file_name, cookbook_name, content|
49
+ Pathname.new(current_dir).join(cookbook_name).should have_structure {
50
+ file "Berksfile" do
51
+ contains content
52
+ end
53
+ file ".chefignore"
54
+ }
55
+ end
56
+
57
+ Then /^the directory "(.*?)" should have the following files:$/ do |name, files|
58
+ check_file_presence(files.raw.map{|file_row| File.join(name, file_row[0])}, true)
59
+ end
60
+
61
+ Then /^the directory "(.*?)" should not have the following files:$/ do |name, files|
62
+ check_file_presence(files.raw.map{|file_row| File.join(name, file_row[0])}, false)
63
+ end
64
+
65
+ Then /^the file "(.*?)" in the directory "(.*?)" should not contain:$/ do |file_name, directory_name, content|
66
+ Pathname.new(current_dir).join(directory_name).should_not have_structure {
67
+ file "Berksfile" do
68
+ contains content
69
+ end
70
+ }
71
+ end
72
+
73
+ Then /^the current directory should have the following files:$/ do |files|
74
+ check_file_presence(files.raw.map{|file_row| file_row[0]}, true)
75
+ end
76
+
77
+ Then /^the current directory should not have the following files:$/ do |files|
78
+ check_file_presence(files.raw.map{|file_row| file_row[0]}, false)
79
+ end
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+ require 'spork'
5
+
6
+ Spork.prefork do
7
+ require 'rspec'
8
+ require 'pp'
9
+ require 'aruba/cucumber'
10
+ require 'vcr'
11
+
12
+ APP_ROOT = File.expand_path('../../../', __FILE__)
13
+
14
+ ENV["BERKSHELF_PATH"] = File.join(APP_ROOT, "tmp", "berkshelf")
15
+
16
+ Dir[File.join(APP_ROOT, "spec/support/**/*.rb")].each {|f| require f}
17
+
18
+ Around do |scenario, block|
19
+ VCR.use_cassette(scenario.title) do
20
+ block.call
21
+ end
22
+ end
23
+
24
+ Before do
25
+ Chef::Config[:chef_server_url] = config['chef_server_url']
26
+ Chef::Config[:client_key] = config['client_key']
27
+ Chef::Config[:node_name] = config['node_name']
28
+ clean_cookbook_store
29
+ @aruba_io_wait_seconds = 5
30
+ end
31
+
32
+ Before('@slow_process') do
33
+ @aruba_timeout_seconds = 15
34
+ @aruba_io_wait_seconds = 10
35
+ end
36
+
37
+ def cookbook_store
38
+ Pathname.new(ENV["BERKSHELF_PATH"])
39
+ end
40
+
41
+ def clean_cookbook_store
42
+ FileUtils.rm_rf(cookbook_store)
43
+ FileUtils.mkdir_p(cookbook_store)
44
+ end
45
+
46
+ def config
47
+ @config ||= YAML.load(File.read(File.join(APP_ROOT, "features", "config.yml")))
48
+ rescue Errno::ENOENT
49
+ raise "Please create a config file at features/config.yml from the sample found at features/config.sample.yml"
50
+ end
51
+ end
52
+
53
+ Spork.each_run do
54
+ require 'berkshelf'
55
+ end
@@ -0,0 +1,23 @@
1
+ Feature: update
2
+ As a user
3
+ I want a way to update the versions without clearing out the files I've downloaded
4
+ So that I can update faster than a clean install
5
+
6
+ Scenario: knife berkshelf update
7
+ Given I write to "Berksfile" with:
8
+ """
9
+ cookbook "mysql"
10
+ """
11
+ Given I write to "Berksfile.lock" with:
12
+ """
13
+ cookbook 'mysql', :locked_version => '0.0.1'
14
+ cookbook 'openssl', :locked_version => '0.0.1'
15
+ """
16
+ When I run the update command
17
+ Then the file "Berksfile.lock" should contain exactly:
18
+ """
19
+ cookbook 'mysql', :locked_version => '1.2.6'
20
+ cookbook 'openssl', :locked_version => '1.0.0'
21
+ cookbook 'chef_handler', :locked_version => '1.0.6'
22
+ cookbook 'windows', :locked_version => '1.3.0'
23
+ """
@@ -0,0 +1,43 @@
1
+ Feature: upload command
2
+ As a Berkshelf CLI user
3
+ I need a way to upload cookbooks to a Chef server that I have installed into my Bookshelf
4
+ So they are available to Chef clients
5
+
6
+ @wip @slow_process
7
+ Scenario: running the upload command when the Sources in the Berksfile are already installed
8
+ Given I write to "Berksfile" with:
9
+ """
10
+ cookbook "mysql", "1.2.4"
11
+ """
12
+ And the Chef server does not have the cookbooks:
13
+ | mysql | 1.2.4 |
14
+ | openssl | 1.0.0 |
15
+ And I run the install command
16
+ When I run the upload command
17
+ Then the output should not contain "Using mysql (1.2.4)"
18
+ And the output should not contain "Using openssl (1.0.0)"
19
+ And the output should contain "Uploading mysql (1.2.4) to:"
20
+ And the output should contain "Uploading openssl (1.0.0) to:"
21
+ And the Chef server should have the cookbooks:
22
+ | mysql | 1.2.4 |
23
+ | openssl | 1.0.0 |
24
+ And the exit status should be 0
25
+
26
+ @slow_process
27
+ Scenario: running the upload command when the Sources in the Berksfile have not been installed
28
+ Given I write to "Berksfile" with:
29
+ """
30
+ cookbook "mysql", "1.2.4"
31
+ """
32
+ And the Chef server does not have the cookbooks:
33
+ | mysql | 1.2.4 |
34
+ | openssl | 1.0.0 |
35
+ When I run the upload command
36
+ Then the output should contain "Installing mysql (1.2.4) from site:"
37
+ And the output should contain "Installing openssl (1.0.0) from site:"
38
+ And the output should contain "Uploading mysql (1.2.4) to:"
39
+ And the output should contain "Uploading openssl (1.0.0) to:"
40
+ And the Chef server should have the cookbooks:
41
+ | mysql | 1.2.4 |
42
+ | openssl | 1.0.0 |
43
+ And the exit status should be 0
@@ -0,0 +1,25 @@
1
+ Feature: --without block
2
+ As a user
3
+ I want to be able to exclude blocks in my Berksfile
4
+ So I can have cookbooks organized for use in different situations in a single Berksfile
5
+
6
+ @slow_process
7
+ Scenario: Exclude a block
8
+ Given I write to "Berksfile" with:
9
+ """
10
+ group :notme do
11
+ cookbook "nginx", "= 0.101.2"
12
+ end
13
+
14
+ cookbook "mysql", "= 1.2.4"
15
+
16
+ group :takeme do
17
+ cookbook "ntp", "= 1.1.8"
18
+ end
19
+ """
20
+ When I run `knife berks install --without notme`
21
+ Then the cookbook store should have the cookbooks:
22
+ | mysql | 1.2.4 |
23
+ | ntp | 1.1.8 |
24
+ And the cookbook store should not have the cookbooks:
25
+ | nginx | 0.101.2 |
@@ -0,0 +1,90 @@
1
+ require 'pathname'
2
+ require 'dep_selector'
3
+ require 'zlib'
4
+ require 'archive/tar/minitar'
5
+ require 'chef/knife'
6
+ require 'chef/rest'
7
+ require 'chef/platform'
8
+ require 'chef/cookbook/metadata'
9
+
10
+ require 'berkshelf/version'
11
+ require 'berkshelf/core_ext'
12
+ require 'berkshelf/errors'
13
+
14
+ Chef::Config[:cache_options][:path] = Dir.mktmpdir
15
+
16
+ module Berkshelf
17
+ DEFAULT_STORE_PATH = File.expand_path("~/.berkshelf").freeze
18
+ DEFAULT_FILENAME = 'Berksfile'.freeze
19
+
20
+ autoload :DSL, 'berkshelf/dsl'
21
+ autoload :Git, 'berkshelf/git'
22
+ autoload :Berksfile, 'berkshelf/berksfile'
23
+ autoload :Lockfile, 'berkshelf/lockfile'
24
+ autoload :InitGenerator, 'berkshelf/init_generator'
25
+ autoload :CookbookSource, 'berkshelf/cookbook_source'
26
+ autoload :CookbookStore, 'berkshelf/cookbook_store'
27
+ autoload :CachedCookbook, 'berkshelf/cached_cookbook'
28
+ autoload :TXResult, 'berkshelf/tx_result'
29
+ autoload :TXResultSet, 'berkshelf/tx_result_set'
30
+ autoload :Downloader, 'berkshelf/downloader'
31
+ autoload :Uploader, 'berkshelf/uploader'
32
+ autoload :Resolver, 'berkshelf/resolver'
33
+
34
+ class << self
35
+ attr_accessor :ui
36
+ attr_accessor :cookbook_store
37
+ attr_accessor :downloader
38
+
39
+ def root
40
+ File.join(File.dirname(__FILE__), '..')
41
+ end
42
+
43
+ def ui
44
+ @ui ||= Chef::Knife::UI.new(null_stream, null_stream, STDIN, {})
45
+ end
46
+
47
+ # Returns the filepath to the location Cookbooks will be downloaded to
48
+ # or uploaded from. By default this is '~/.berkshelf' but can be overridden
49
+ # by specifying a value for the ENV variable 'BERKSHELF_PATH'.
50
+ #
51
+ # @return [String]
52
+ def berkshelf_path
53
+ ENV["BERKSHELF_PATH"] || DEFAULT_STORE_PATH
54
+ end
55
+
56
+ def cookbook_store
57
+ @cookbook_store ||= CookbookStore.new(berkshelf_path)
58
+ end
59
+
60
+ def downloader
61
+ @downloader ||= Downloader.new(cookbook_store)
62
+ end
63
+
64
+ # Ascend the directory structure from the given path to find a
65
+ # metadata.rb file of a Chef Cookbook. If no metadata.rb file
66
+ # was found, nil is returned.
67
+ #
68
+ # @return [Pathname]
69
+ # path to metadata.rb
70
+ def find_metadata(path = Dir.pwd)
71
+ path = Pathname.new(path)
72
+ path.ascend do |potential_root|
73
+ if potential_root.entries.collect(&:to_s).include?('metadata.rb')
74
+ return potential_root.join('metadata.rb')
75
+ end
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def null_stream
82
+ @null ||= begin
83
+ strm = STDOUT.clone
84
+ strm.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
85
+ strm.sync = true
86
+ strm
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,170 @@
1
+ module Berkshelf
2
+ # @author Jamie Winsor <jamie@vialstudios.com>
3
+ class Berksfile
4
+ include DSL
5
+
6
+ class << self
7
+ def from_file(file)
8
+ content = File.read(file)
9
+ read(content)
10
+ rescue Errno::ENOENT => e
11
+ raise BerksfileNotFound, "No Berksfile or Berksfile.lock found at: #{file}"
12
+ end
13
+
14
+ def read(content)
15
+ object = new
16
+ object.instance_eval(content)
17
+
18
+ object
19
+ end
20
+
21
+ # @param [Array] sources
22
+ # an array of sources to filter
23
+ # @param [Array, Symbol] excluded
24
+ # an array of symbols or a symbol representing the group or group(s)
25
+ # to exclude
26
+ #
27
+ # @return [Array<Berkshelf::CookbookSource>]
28
+ # an array of sources that are not members of the excluded group(s)
29
+ def filter_sources(sources, excluded)
30
+ excluded = Array(excluded)
31
+ excluded.collect!(&:to_sym)
32
+
33
+ sources.select { |source| (excluded & source.groups).empty? }
34
+ end
35
+ end
36
+
37
+ def initialize
38
+ @sources = Hash.new
39
+ end
40
+
41
+ # Add the given source to the sources array. A DuplicateSourceDefined
42
+ # exception will be raised if a source is added whose name conflicts
43
+ # with a source who has already been added.
44
+ #
45
+ # @param [Berkshelf::CookbookSource] source
46
+ # the source to add
47
+ #
48
+ # @return [Array<Berkshelf::CookbookSource]
49
+ def add_source(source)
50
+ raise DuplicateSourceDefined if has_source?(source)
51
+ @sources[source.to_s] = source
52
+ end
53
+
54
+ # @param [#to_s] source
55
+ # the source to remove
56
+ #
57
+ # @return [Berkshelf::CookbookSource]
58
+ def remove_source(source)
59
+ @sources.delete(source.to_s)
60
+ end
61
+
62
+ # @param [#to_s] source
63
+ # the source to check presence of
64
+ #
65
+ # @return [Boolean]
66
+ def has_source?(source)
67
+ @sources.has_key?(source.to_s)
68
+ end
69
+
70
+ # @param [Hash] options
71
+ # a hash of options
72
+ #
73
+ # Options:
74
+ # exclude: An array of groups to exclude from the returned Array
75
+ # of sources
76
+ #
77
+ # @return [Array<Berkshelf::CookbookSource>]
78
+ def sources(options = {})
79
+ l_sources = @sources.collect { |name, source| source }.flatten
80
+
81
+ if options[:exclude]
82
+ self.class.filter_sources(l_sources, options[:exclude])
83
+ else
84
+ l_sources
85
+ end
86
+ end
87
+
88
+ # @return [Hash]
89
+ # a hash containing group names as keys and an array of CookbookSources
90
+ # that are a member of that group as values
91
+ #
92
+ # Example:
93
+ # {
94
+ # nautilus: [
95
+ # #<Berkshelf::CookbookSource @name="nginx">,
96
+ # #<Berkshelf::CookbookSource @name="mysql">,
97
+ # ],
98
+ # skarner: [
99
+ # #<Berkshelf::CookbookSource @name="nginx">
100
+ # ]
101
+ # }
102
+ def groups
103
+ {}.tap do |groups|
104
+ @sources.each_pair do |name, source|
105
+ source.groups.each do |group|
106
+ groups[group] ||= []
107
+ groups[group] << source
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ # @param [String] name
114
+ # name of the source to return
115
+ #
116
+ # @return [Berkshelf::CookbookSource]
117
+ def [](name)
118
+ @sources[name]
119
+ end
120
+ alias_method :get_source, :[]
121
+
122
+ # @param [Hash] options
123
+ # a hash of options
124
+ #
125
+ # Options:
126
+ # without: An array of groups to exclude which will cause any sources
127
+ # marked as a member of the group to not be installed
128
+ def install(options = {})
129
+ resolver = Resolver.new(Berkshelf.downloader, sources(exclude: options[:without]))
130
+ resolver.resolve
131
+ write_lockfile(resolver.sources) unless lockfile_present?
132
+ end
133
+
134
+ # @param [String] chef_server_url
135
+ # the full URL to the Chef Server to upload to
136
+ #
137
+ # Example:
138
+ # "https://api.opscode.com/organizations/vialstudios"
139
+ #
140
+ # @param [Hash] options
141
+ # a hash of options
142
+ #
143
+ # Options:
144
+ # without: An array of groups to exclude which will cause any sources
145
+ # marked as a member of the group to not be installed
146
+ # force: Upload the Cookbook even if the version already exists and is
147
+ # frozen on the target Chef Server
148
+ # freeze: Freeze the uploaded Cookbook on the Chef Server so that it
149
+ # cannot be overwritten
150
+ def upload(chef_server_url, options = {})
151
+ uploader = Uploader.new(Berkshelf.cookbook_store, chef_server_url)
152
+ resolver = Resolver.new(Berkshelf.downloader, sources(exclude: options[:without]))
153
+
154
+ resolver.resolve.each do |name, version|
155
+ Berkshelf.ui.info "Uploading #{name} (#{version}) to: #{chef_server_url}"
156
+ uploader.upload!(name, version, options)
157
+ end
158
+ end
159
+
160
+ private
161
+
162
+ def lockfile_present?
163
+ File.exist?(Berkshelf::Lockfile::DEFAULT_FILENAME)
164
+ end
165
+
166
+ def write_lockfile(sources)
167
+ Berkshelf::Lockfile.new(sources).write
168
+ end
169
+ end
170
+ end