berkshelf 3.0.0.beta7 → 3.0.0.beta8
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +4 -1
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +0 -1
- data/Guardfile +0 -8
- data/README.md +33 -13
- data/berkshelf.gemspec +3 -3
- data/features/commands/install.feature +16 -88
- data/features/commands/search.feature +15 -0
- data/features/commands/shelf/show.feature +2 -2
- data/features/commands/shelf/uninstall.feature +1 -1
- data/features/commands/show.feature +3 -3
- data/features/commands/update.feature +29 -1
- data/features/commands/upload.feature +172 -7
- data/features/commands/vendor.feature +32 -0
- data/features/json_formatter.feature +26 -24
- data/features/lifecycle.feature +285 -0
- data/features/lockfile.feature +9 -7
- data/features/step_definitions/chef_server_steps.rb +1 -0
- data/features/step_definitions/cli_steps.rb +2 -2
- data/features/step_definitions/filesystem_steps.rb +2 -4
- data/gem_graph.png +0 -0
- data/generator_files/chefignore +0 -2
- data/lib/berkshelf.rb +39 -14
- data/lib/berkshelf/berksfile.rb +161 -113
- data/lib/berkshelf/cached_cookbook.rb +2 -2
- data/lib/berkshelf/cli.rb +15 -3
- data/lib/berkshelf/commands/shelf.rb +3 -7
- data/lib/berkshelf/community_rest.rb +9 -9
- data/lib/berkshelf/config.rb +3 -3
- data/lib/berkshelf/cookbook_generator.rb +0 -8
- data/lib/berkshelf/cookbook_store.rb +1 -2
- data/lib/berkshelf/dependency.rb +25 -138
- data/lib/berkshelf/downloader.rb +41 -7
- data/lib/berkshelf/errors.rb +113 -214
- data/lib/berkshelf/formatters/base.rb +42 -0
- data/lib/berkshelf/formatters/human.rb +145 -0
- data/lib/berkshelf/formatters/json.rb +149 -133
- data/lib/berkshelf/formatters/null.rb +8 -18
- data/lib/berkshelf/init_generator.rb +1 -1
- data/lib/berkshelf/installer.rb +115 -104
- data/lib/berkshelf/location.rb +22 -121
- data/lib/berkshelf/locations/base.rb +75 -0
- data/lib/berkshelf/locations/git.rb +196 -0
- data/lib/berkshelf/locations/github.rb +8 -0
- data/lib/berkshelf/locations/path.rb +78 -0
- data/lib/berkshelf/lockfile.rb +452 -290
- data/lib/berkshelf/logger.rb +9 -3
- data/lib/berkshelf/mixin/logging.rb +4 -9
- data/lib/berkshelf/resolver.rb +12 -12
- data/lib/berkshelf/source.rb +13 -1
- data/lib/berkshelf/version.rb +1 -1
- data/spec/fixtures/cookbooks/example_cookbook-0.5.0/metadata.rb +3 -7
- data/spec/fixtures/cookbooks/example_cookbook/metadata.rb +3 -6
- data/spec/spec_helper.rb +5 -6
- data/spec/support/matchers/file_system_matchers.rb +4 -0
- data/spec/support/shared_examples/formatter.rb +11 -0
- data/spec/unit/berkshelf/berksfile_spec.rb +25 -28
- data/spec/unit/berkshelf/cli_spec.rb +19 -11
- data/spec/unit/berkshelf/dependency_spec.rb +4 -164
- data/spec/unit/berkshelf/formatters/base_spec.rb +35 -0
- data/spec/unit/berkshelf/formatters/human_spec.rb +7 -0
- data/spec/unit/berkshelf/formatters/json_spec.rb +7 -0
- data/spec/unit/berkshelf/formatters/null_spec.rb +7 -11
- data/spec/unit/berkshelf/location_spec.rb +16 -144
- data/spec/unit/berkshelf/locations/base_spec.rb +80 -0
- data/spec/unit/berkshelf/locations/git_spec.rb +249 -0
- data/spec/unit/berkshelf/locations/path_spec.rb +107 -0
- data/spec/unit/berkshelf/lockfile_parser_spec.rb +3 -3
- data/spec/unit/berkshelf/lockfile_spec.rb +55 -11
- data/spec/unit/berkshelf/logger_spec.rb +2 -2
- data/spec/unit/berkshelf/mixin/logging_spec.rb +5 -9
- data/spec/unit/berkshelf/source_spec.rb +32 -13
- data/spec/unit/berkshelf_spec.rb +6 -9
- metadata +33 -33
- data/.ruby-version +0 -1
- data/berkshelf-complete.sh +0 -75
- data/lib/berkshelf/formatters.rb +0 -110
- data/lib/berkshelf/formatters/human_readable.rb +0 -142
- data/lib/berkshelf/git.rb +0 -204
- data/lib/berkshelf/locations/git_location.rb +0 -135
- data/lib/berkshelf/locations/github_location.rb +0 -55
- data/lib/berkshelf/locations/mercurial_location.rb +0 -114
- data/lib/berkshelf/locations/path_location.rb +0 -88
- data/lib/berkshelf/mercurial.rb +0 -146
- data/lib/berkshelf/mixin.rb +0 -7
- data/spec/support/mercurial.rb +0 -123
- data/spec/unit/berkshelf/formatters_spec.rb +0 -114
- data/spec/unit/berkshelf/git_spec.rb +0 -312
- data/spec/unit/berkshelf/locations/git_location_spec.rb +0 -126
- data/spec/unit/berkshelf/locations/mercurial_location_spec.rb +0 -131
- data/spec/unit/berkshelf/locations/path_location_spec.rb +0 -25
- data/spec/unit/berkshelf/mercurial_spec.rb +0 -172
@@ -1,22 +1,12 @@
|
|
1
1
|
module Berkshelf
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# otherwise they will be inherited by the Ruby object model.
|
11
|
-
AbstractFormatter.instance_methods.each do |meth|
|
12
|
-
define_method(meth) do |*args|
|
13
|
-
# intentionally do nothing
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def method_missing(meth, *args, &block)
|
18
|
-
# intentionally do nothing
|
19
|
-
end
|
2
|
+
class NullFormatter < BaseFormatter
|
3
|
+
# The base formatter dynamically defines methods that raise an
|
4
|
+
# AbstractFunction error. We need to define all of those on our class,
|
5
|
+
# otherwise they will be inherited by the Ruby object model.
|
6
|
+
BaseFormatter.instance_methods(false).each do |name|
|
7
|
+
class_eval <<-EOH, __FILE__, __LINE__ + 1
|
8
|
+
def #{name}(*args); end
|
9
|
+
EOH
|
20
10
|
end
|
21
11
|
end
|
22
12
|
end
|
data/lib/berkshelf/installer.rb
CHANGED
@@ -29,134 +29,145 @@ module Berkshelf
|
|
29
29
|
|
30
30
|
# @return [Array<Berkshelf::CachedCookbook>]
|
31
31
|
def run
|
32
|
-
|
32
|
+
lockfile.reduce!
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
Berkshelf.formatter.msg('Resolving cookbook dependencies...')
|
35
|
+
|
36
|
+
dependencies, cookbooks = if lockfile.trusted?
|
37
|
+
install_from_lockfile
|
38
|
+
else
|
39
|
+
install_from_universe
|
40
|
+
end
|
41
|
+
|
42
|
+
Berkshelf.log.debug " Finished resolving, calculating locks"
|
43
|
+
|
44
|
+
to_lock = dependencies.select do |dependency|
|
45
|
+
berksfile.has_dependency?(dependency)
|
46
|
+
end
|
47
|
+
|
48
|
+
Berkshelf.log.debug " New locks"
|
49
|
+
to_lock.each do |lock|
|
50
|
+
Berkshelf.log.debug " #{lock}"
|
38
51
|
end
|
39
52
|
|
40
53
|
lockfile.graph.update(cookbooks)
|
41
|
-
lockfile.update(
|
54
|
+
lockfile.update(to_lock)
|
42
55
|
lockfile.save
|
43
56
|
|
44
57
|
cookbooks
|
45
58
|
end
|
46
59
|
|
47
|
-
|
48
|
-
#
|
49
|
-
# @return [Array<CachedCookbook>]
|
50
|
-
# the list of installed cookbooks
|
51
|
-
def install_from_lockfile
|
52
|
-
locks = lockfile.graph.locks
|
60
|
+
private
|
53
61
|
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
|
62
|
+
# Install a specific dependency.
|
63
|
+
#
|
64
|
+
# @param [Dependency]
|
65
|
+
# the dependency to install
|
66
|
+
# @return [CachedCookbook]
|
67
|
+
# the installed cookbook
|
68
|
+
def install(dependency)
|
69
|
+
Berkshelf.log.info "Installing #{dependency}"
|
58
70
|
|
59
|
-
|
60
|
-
|
61
|
-
end
|
62
|
-
end
|
71
|
+
if dependency.installed?
|
72
|
+
Berkshelf.log.debug " Already installed - skipping install"
|
63
73
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
hash[dependency.name] ||= dependency
|
74
|
-
hash
|
75
|
-
end.values
|
76
|
-
|
77
|
-
resolver = Resolver.new(berksfile, dependencies)
|
78
|
-
|
79
|
-
# Download all SCM locations first, since they might have additional
|
80
|
-
# constraints that we don't yet know about
|
81
|
-
dependencies.select(&:scm_location?).each do |dependency|
|
82
|
-
Berkshelf.formatter.fetch(dependency)
|
83
|
-
dependency.download
|
84
|
-
end
|
74
|
+
Berkshelf.formatter.use(dependency)
|
75
|
+
dependency.cached_cookbook
|
76
|
+
else
|
77
|
+
name, version = dependency.name, dependency.locked_version.to_s
|
78
|
+
source = berksfile.source_for(name, version)
|
79
|
+
|
80
|
+
Berkshelf.log.debug " Downloading #{dependency.name} (#{dependency.locked_version}) from #{source}"
|
81
|
+
|
82
|
+
cookbook = source.cookbook(name, version)
|
85
83
|
|
86
|
-
|
87
|
-
# the universe when installing from the universe... duh
|
88
|
-
build_universe
|
84
|
+
Berkshelf.log.debug " => #{cookbook.inspect}"
|
89
85
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
resolver.add_explicit_dependencies(cookbook)
|
86
|
+
Berkshelf.formatter.install(source, cookbook)
|
87
|
+
|
88
|
+
stash = downloader.download(name, version)
|
89
|
+
CookbookStore.import(name, version, stash)
|
95
90
|
end
|
96
91
|
end
|
97
92
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
93
|
+
# Install all the dependencies from the lockfile graph.
|
94
|
+
#
|
95
|
+
# @return [Array<Array<Dependency> Array<CachedCookbook>>]
|
96
|
+
# the list of installed dependencies and cookbooks
|
97
|
+
def install_from_lockfile
|
98
|
+
Berkshelf.log.info "Installing from lockfile"
|
99
|
+
|
100
|
+
dependencies = lockfile.graph.locks.values
|
101
|
+
|
102
|
+
Berkshelf.log.debug " Dependencies"
|
103
|
+
dependencies.map do |dependency|
|
104
|
+
Berkshelf.log.debug " #{dependency}"
|
105
|
+
end
|
106
|
+
|
107
|
+
download_locations(dependencies)
|
102
108
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
name, version = dependency.name, dependency.locked_version.to_s
|
115
|
-
source = berksfile.source_for(name, version)
|
116
|
-
cookbook = source.cookbook(name, version)
|
117
|
-
|
118
|
-
Berkshelf.formatter.install(source, cookbook)
|
119
|
-
|
120
|
-
stash = downloader.download(name, version)
|
121
|
-
CookbookStore.import(name, version, stash)
|
109
|
+
# Only construct the universe if we are going to install things
|
110
|
+
unless dependencies.all?(&:installed?)
|
111
|
+
Berkshelf.log.debug " Not all dependencies are installed"
|
112
|
+
build_universe
|
113
|
+
end
|
114
|
+
|
115
|
+
cookbooks = dependencies.sort.collect do |dependency|
|
116
|
+
install(dependency)
|
117
|
+
end
|
118
|
+
|
119
|
+
[dependencies, cookbooks]
|
122
120
|
end
|
123
|
-
end
|
124
121
|
|
125
|
-
|
122
|
+
# Resolve and install the dependencies from the "universe", updating the
|
123
|
+
# lockfile appropiately.
|
124
|
+
#
|
125
|
+
# @return [Array<Array<Dependency> Array<CachedCookbook>>]
|
126
|
+
# the list of installed dependencies and cookbooks
|
127
|
+
def install_from_universe
|
128
|
+
Berkshelf.log.info "Installing from universe"
|
129
|
+
|
130
|
+
dependencies = lockfile.graph.locks.values + berksfile.dependencies
|
131
|
+
dependencies = dependencies.inject({}) do |hash, dependency|
|
132
|
+
# Fancy way of ensuring no duplicate dependencies are used...
|
133
|
+
hash[dependency.name] ||= dependency
|
134
|
+
hash
|
135
|
+
end.values
|
136
|
+
|
137
|
+
download_locations(dependencies)
|
138
|
+
|
139
|
+
Berkshelf.log.debug " Creating a resolver"
|
140
|
+
resolver = Resolver.new(berksfile, dependencies)
|
141
|
+
|
142
|
+
# Unlike when installing from the lockfile, we _always_ need to build
|
143
|
+
# the universe when installing from the universe... duh
|
144
|
+
build_universe
|
126
145
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
# from the Berksfile, but still remained locked in the lockfile.
|
134
|
-
#
|
135
|
-
# If the dependency exists, a constraint comparison is conducted to verify
|
136
|
-
# that the locked dependency still satisifes the original constraint. This
|
137
|
-
# handles the edge case where a user has updated or removed a constraint
|
138
|
-
# on a dependency that already existed in the lockfile.
|
139
|
-
#
|
140
|
-
# @raise [OutdatedDependency]
|
141
|
-
# if the constraint exists, but is no longer satisifed by the existing
|
142
|
-
# locked version
|
143
|
-
#
|
144
|
-
# @return [Array<Dependency>]
|
145
|
-
def reduce_lockfile!
|
146
|
-
lockfile.dependencies.each do |dependency|
|
147
|
-
if berksfile.dependencies.map(&:name).include?(dependency.name)
|
148
|
-
locked = lockfile.graph.find(dependency)
|
149
|
-
next if locked.nil?
|
150
|
-
|
151
|
-
unless dependency.version_constraint.satisfies?(locked.version)
|
152
|
-
raise OutdatedDependency.new(locked, dependency)
|
146
|
+
# Add any explicit dependencies for already-downloaded cookbooks (like
|
147
|
+
# path locations)
|
148
|
+
dependencies.each do |dependency|
|
149
|
+
if cookbook = dependency.cached_cookbook
|
150
|
+
Berkshelf.log.debug " Adding explicit dependency on #{cookbook}"
|
151
|
+
resolver.add_explicit_dependencies(cookbook)
|
153
152
|
end
|
154
|
-
else
|
155
|
-
lockfile.unlock(dependency)
|
156
153
|
end
|
154
|
+
|
155
|
+
Berkshelf.log.debug " Starting resolution..."
|
156
|
+
|
157
|
+
cookbooks = resolver.resolve.sort.collect do |dependency|
|
158
|
+
install(dependency)
|
159
|
+
end
|
160
|
+
|
161
|
+
[dependencies, cookbooks]
|
157
162
|
end
|
158
163
|
|
159
|
-
|
160
|
-
|
164
|
+
def download_locations(dependencies)
|
165
|
+
dependencies.select(&:location).each do |dependency|
|
166
|
+
unless dependency.location.installed?
|
167
|
+
Berkshelf.formatter.fetch(dependency)
|
168
|
+
dependency.location.install
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
161
172
|
end
|
162
173
|
end
|
data/lib/berkshelf/location.rb
CHANGED
@@ -1,146 +1,47 @@
|
|
1
1
|
module Berkshelf
|
2
|
-
|
2
|
+
class Location
|
3
3
|
class << self
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# Berkshelf::Dependency. If your options Hash contains a key matching one of these location_keys
|
8
|
-
# then the Class who registered that location_key will be instantiated. If you do not
|
9
|
-
# provide an option with a matching location_key nil will be returned.
|
4
|
+
# Create a new instance of a Location class given dependency and options.
|
5
|
+
# The type of class is determined by the values in the given +options+
|
6
|
+
# Hash.
|
10
7
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# instantiates a GitLocation
|
8
|
+
# If you do not provide an option with a matching location id, +nil+
|
9
|
+
# is returned.
|
14
10
|
#
|
15
|
-
#
|
16
|
-
#
|
11
|
+
# @example Create a git location
|
12
|
+
# Location.init(dependency, git: 'git://github.com/berkshelf/berkshelf.git')
|
17
13
|
#
|
18
|
-
#
|
19
|
-
#
|
14
|
+
# @example Create a GitHub location
|
15
|
+
# Location.init(dependency, github: 'berkshelf/berkshelf')
|
20
16
|
#
|
21
17
|
# @param [Dependency] dependency
|
22
18
|
# @param [Hash] options
|
23
19
|
#
|
24
|
-
# @return [~
|
20
|
+
# @return [~BaseLocation, nil]
|
25
21
|
def init(dependency, options = {})
|
26
22
|
if klass = klass_from_options(options)
|
27
23
|
klass.new(dependency, options)
|
24
|
+
else
|
25
|
+
nil
|
28
26
|
end
|
29
27
|
end
|
30
28
|
|
31
29
|
private
|
32
30
|
|
33
|
-
|
34
|
-
location_keys = (options.keys & Berkshelf::Dependency.location_keys.keys)
|
35
|
-
if location_keys.length > 1
|
36
|
-
location_keys.collect! { |opt| "'#{opt}'" }
|
37
|
-
raise InternalError, "Only one location key (#{Berkshelf::Dependency.location_keys.keys.join(', ')}) " +
|
38
|
-
"may be specified. You gave #{location_keys.join(', ')}."
|
39
|
-
end
|
40
|
-
|
41
|
-
if location_key = location_keys.first
|
42
|
-
Berkshelf::Dependency.location_keys[location_key]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
class Base
|
48
|
-
class << self
|
49
|
-
# Returns the location identifier key for the class
|
50
|
-
#
|
51
|
-
# @return [Symbol]
|
52
|
-
attr_reader :location_key
|
53
|
-
|
54
|
-
# Register the location key for the including source location with Berkshelf::Dependency
|
31
|
+
# Load the correct location from the given options.
|
55
32
|
#
|
56
|
-
# @
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
end
|
33
|
+
# @return [Class, nil]
|
34
|
+
def klass_from_options(options)
|
35
|
+
options.each do |key, _|
|
36
|
+
id = key.to_s.capitalize
|
61
37
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def set_valid_options(*opts)
|
66
|
-
Array(opts).each do |opt|
|
67
|
-
Berkshelf::Dependency.add_valid_option(opt)
|
38
|
+
begin
|
39
|
+
return Berkshelf.const_get("#{id}Location")
|
40
|
+
rescue NameError; end
|
68
41
|
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
extend Forwardable
|
73
|
-
|
74
|
-
attr_reader :dependency
|
75
|
-
def_delegator :dependency, :name
|
76
|
-
|
77
|
-
# @param [Berkshelf::Dependency] dependency
|
78
|
-
# @param [Hash] options
|
79
|
-
def initialize(dependency, options = {})
|
80
|
-
@dependency = dependency
|
81
|
-
@@cached_cookbook = nil
|
82
|
-
end
|
83
|
-
|
84
|
-
# @param [#to_s] destination
|
85
|
-
#
|
86
|
-
# @return [Berkshelf::CachedCookbook]
|
87
|
-
def download
|
88
|
-
return @cached_cookbook if @cached_cookbook
|
89
|
-
|
90
|
-
cached_cookbook = do_download
|
91
|
-
validate_cached(cached_cookbook)
|
92
|
-
@cached_cookbook = cached_cookbook
|
93
|
-
end
|
94
|
-
|
95
|
-
# @param [#to_s] destination
|
96
|
-
#
|
97
|
-
# @return [Berkshelf::CachedCookbook]
|
98
|
-
def do_download
|
99
|
-
raise AbstractFunction
|
100
|
-
end
|
101
42
|
|
102
|
-
|
103
|
-
#
|
104
|
-
# @param [CachedCookbook] cached_cookbook
|
105
|
-
# the downloaded cookbook to validate
|
106
|
-
#
|
107
|
-
# @raise [CookbookValidationFailure] if given CachedCookbook does not satisfy the constraint of the location
|
108
|
-
#
|
109
|
-
# @todo Change MismatchedCookbookName to raise instead of warn
|
110
|
-
#
|
111
|
-
# @return [Boolean]
|
112
|
-
def validate_cached(cached_cookbook)
|
113
|
-
unless dependency.version_constraint.satisfies?(cached_cookbook.version)
|
114
|
-
raise CookbookValidationFailure.new(dependency, cached_cookbook)
|
43
|
+
nil
|
115
44
|
end
|
116
|
-
|
117
|
-
unless dependency.name == cached_cookbook.cookbook_name
|
118
|
-
Berkshelf.ui.warn(MismatchedCookbookName.new(dependency, cached_cookbook).to_s)
|
119
|
-
end
|
120
|
-
|
121
|
-
true
|
122
|
-
end
|
123
|
-
|
124
|
-
# Determines if the location is well formed and points to an accessible location
|
125
|
-
#
|
126
|
-
# @return [Boolean]
|
127
|
-
def valid?
|
128
|
-
true
|
129
|
-
end
|
130
|
-
|
131
|
-
def to_hash
|
132
|
-
{ type: self.class.location_key }
|
133
|
-
end
|
134
|
-
|
135
|
-
def to_json(options = {})
|
136
|
-
JSON.pretty_generate(to_hash, options)
|
137
|
-
end
|
138
45
|
end
|
139
|
-
|
140
|
-
class ScmLocation < Location::Base; end
|
141
46
|
end
|
142
47
|
end
|
143
|
-
|
144
|
-
Dir["#{File.dirname(__FILE__)}/locations/*.rb"].sort.each do |path|
|
145
|
-
require_relative "locations/#{File.basename(path, '.rb')}"
|
146
|
-
end
|