berkshelf 3.0.0.beta7 → 3.0.0.beta8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|