librarian 0.0.25 → 0.0.26
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.
- data/.gitignore +4 -0
- data/CHANGELOG.md +21 -0
- data/README.md +6 -1
- data/lib/librarian/action/persist_resolution_mixin.rb +51 -0
- data/lib/librarian/action/resolve.rb +3 -38
- data/lib/librarian/action/update.rb +4 -38
- data/lib/librarian/chef/dsl.rb +1 -0
- data/lib/librarian/chef/source.rb +1 -0
- data/lib/librarian/chef/source/github.rb +27 -0
- data/lib/librarian/chef/source/site.rb +51 -51
- data/lib/librarian/cli.rb +31 -23
- data/lib/librarian/cli/manifest_presenter.rb +36 -22
- data/lib/librarian/dependency.rb +60 -0
- data/lib/librarian/environment.rb +13 -1
- data/lib/librarian/linter/source_linter.rb +55 -0
- data/lib/librarian/lockfile/parser.rb +39 -16
- data/lib/librarian/manifest.rb +8 -0
- data/lib/librarian/manifest_set.rb +5 -7
- data/lib/librarian/mock/source/mock.rb +4 -21
- data/lib/librarian/resolution.rb +1 -1
- data/lib/librarian/resolver.rb +15 -12
- data/lib/librarian/resolver/implementation.rb +166 -75
- data/lib/librarian/source/basic_api.rb +45 -0
- data/lib/librarian/source/git.rb +4 -22
- data/lib/librarian/source/git/repository.rb +1 -1
- data/lib/librarian/source/local.rb +0 -7
- data/lib/librarian/source/path.rb +4 -22
- data/lib/librarian/version.rb +1 -1
- data/librarian.gemspec +3 -3
- data/spec/functional/chef/source/site_spec.rb +150 -100
- data/spec/functional/source/git/repository_spec.rb +2 -1
- data/spec/{functional → integration}/chef/source/git_spec.rb +12 -3
- data/spec/integration/chef/source/site_spec.rb +217 -0
- data/spec/support/cli_macro.rb +4 -12
- data/spec/support/method_patch_macro.rb +30 -0
- data/spec/unit/config/database_spec.rb +8 -0
- data/spec/unit/dependency_spec.rb +176 -0
- data/spec/unit/environment_spec.rb +76 -7
- data/spec/unit/resolver_spec.rb +2 -2
- metadata +52 -46
@@ -1,32 +1,15 @@
|
|
1
1
|
require 'librarian/manifest'
|
2
|
+
require 'librarian/source/basic_api'
|
2
3
|
require 'librarian/mock/source/mock/registry'
|
3
4
|
|
4
5
|
module Librarian
|
5
6
|
module Mock
|
6
7
|
module Source
|
7
8
|
class Mock
|
9
|
+
include Librarian::Source::BasicApi
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
LOCK_NAME = 'MOCK'
|
12
|
-
|
13
|
-
def lock_name
|
14
|
-
LOCK_NAME
|
15
|
-
end
|
16
|
-
|
17
|
-
def from_lock_options(environment, options)
|
18
|
-
new(environment, options[:remote], options.reject{|k, v| k == :remote})
|
19
|
-
end
|
20
|
-
|
21
|
-
def from_spec_args(environment, name, options)
|
22
|
-
recognized_options = []
|
23
|
-
unrecognized_options = options.keys - recognized_options
|
24
|
-
unrecognized_options.empty? or raise Error, "unrecognized options: #{unrecognized_options.join(", ")}"
|
25
|
-
|
26
|
-
new(environment, name, options)
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
11
|
+
lock_name 'MOCK'
|
12
|
+
spec_options []
|
30
13
|
|
31
14
|
attr_accessor :environment
|
32
15
|
private :environment=
|
data/lib/librarian/resolution.rb
CHANGED
data/lib/librarian/resolver.rb
CHANGED
@@ -13,19 +13,24 @@ module Librarian
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def resolve(spec, partial_manifests = [])
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
manifests = implementation(spec).resolve(partial_manifests)
|
17
|
+
if manifests
|
18
|
+
enforce_consistency!(spec.dependencies, manifests)
|
19
|
+
manifests = sort(manifests)
|
20
|
+
Resolution.new(spec.dependencies, manifests)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def implementation(spec)
|
27
|
+
Implementation.new(self, spec)
|
22
28
|
end
|
23
29
|
|
24
30
|
def enforce_consistency!(dependencies, manifests)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
} && ManifestSet.new(manifests).consistent?
|
31
|
+
manifest_set = ManifestSet.new(manifests)
|
32
|
+
return if manifest_set.in_compliance_with?(dependencies)
|
33
|
+
return if manifest_set.consistent?
|
29
34
|
|
30
35
|
debug { "Resolver Malfunctioned!" }
|
31
36
|
errors = []
|
@@ -68,8 +73,6 @@ module Librarian
|
|
68
73
|
ManifestSet.sort(manifests)
|
69
74
|
end
|
70
75
|
|
71
|
-
private
|
72
|
-
|
73
76
|
def debug(*args, &block)
|
74
77
|
environment.logger.debug(*args, &block)
|
75
78
|
end
|
@@ -10,29 +10,114 @@ module Librarian
|
|
10
10
|
self.sources = sources
|
11
11
|
end
|
12
12
|
def manifests(name)
|
13
|
-
sources.reverse.map{|source| source.manifests(name)}.flatten(1)
|
13
|
+
sources.reverse.map{|source| source.manifests(name)}.flatten(1).compact
|
14
|
+
end
|
15
|
+
def to_s
|
16
|
+
"(no source specified)"
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
17
|
-
|
20
|
+
attr_accessor :resolver, :spec
|
21
|
+
private :resolver=, :spec=
|
18
22
|
|
19
23
|
def initialize(resolver, spec)
|
20
|
-
|
21
|
-
|
22
|
-
@dependency_source_map = Hash[spec.dependencies.map{|d| [d.name, d.source]}]
|
24
|
+
self.resolver = resolver
|
25
|
+
self.spec = spec
|
23
26
|
@level = 0
|
24
27
|
end
|
25
28
|
|
26
|
-
def resolve(
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
def resolve(manifests)
|
30
|
+
manifests = index_by(manifests, &:name) if manifests.kind_of?(Array)
|
31
|
+
addtl = spec.dependencies + sourced_dependencies_for_manifests(manifests)
|
32
|
+
recursive_resolve([], manifests, [], addtl)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def find_inconsistency(dep, deps, mans)
|
38
|
+
m = mans[dep.name]
|
39
|
+
dep.satisfied_by?(m) or return m if m
|
40
|
+
deps.find{|d| !dep.consistent_with?(d)}
|
41
|
+
end
|
42
|
+
|
43
|
+
def recursive_resolve(dependencies, manifests, queue, addtl)
|
44
|
+
dependencies = dependencies.dup
|
45
|
+
manifests = manifests.dup
|
46
|
+
queue = queue.dup
|
47
|
+
|
48
|
+
return unless enqueue_dependencies(queue, addtl, dependencies, manifests)
|
49
|
+
return unless shift_resolved_enqueued_dependencies(dependencies, manifests, queue)
|
50
|
+
return manifests if queue.empty?
|
51
|
+
|
52
|
+
dependency = queue.shift
|
53
|
+
dependencies << dependency
|
54
|
+
all_deps = dependencies + queue
|
55
|
+
|
56
|
+
resolving_dependency_map_find_manifests(dependency) do |manifest|
|
57
|
+
next unless check_manifest(manifest, all_deps)
|
58
|
+
|
59
|
+
m = manifests.merge(dependency.name => manifest)
|
60
|
+
a = sourced_dependencies_for_manifest(manifest)
|
61
|
+
|
62
|
+
recursive_resolve(dependencies, m, queue, a)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# When using this method, you are required to check the return value.
|
67
|
+
# Returns +true+ if the enqueueables could all be enqueued.
|
68
|
+
# Returns +false+ if there was an inconsistency when trying to enqueue one
|
69
|
+
# or more of them.
|
70
|
+
# This modifies +queue+ but does not modify any other arguments.
|
71
|
+
def enqueue_dependencies(queue, enqueueables, dependencies, manifests)
|
72
|
+
enqueueables.each do |d|
|
73
|
+
if q = find_inconsistency(d, dependencies + queue, manifests)
|
74
|
+
debug_conflict d, q
|
75
|
+
return false
|
76
|
+
end
|
77
|
+
debug_schedule d
|
78
|
+
queue << d
|
79
|
+
end
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
# When using this method, you are required to check the return value.
|
84
|
+
# Returns +true+ if the resolved enqueued dependencies at the front of the
|
85
|
+
# queue could all be moved to the resolved dependencies list.
|
86
|
+
# Returns +false+ if there was an inconsistency when trying to move one or
|
87
|
+
# more of them.
|
88
|
+
# This modifies +queue+ and +dependencies+.
|
89
|
+
def shift_resolved_enqueued_dependencies(dependencies, manifests, queue)
|
90
|
+
all_deps = dependencies + queue
|
91
|
+
while (dependency = queue.first) && manifests[dependency.name]
|
92
|
+
if q = find_inconsistency(dependency, all_deps, manifests)
|
93
|
+
debug_conflict dependency, q
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
dependencies << queue.shift
|
97
|
+
end
|
98
|
+
true
|
99
|
+
end
|
100
|
+
|
101
|
+
# When using this method, you are required to check the return value.
|
102
|
+
# Returns +true+ if the manifest satisfies all of the dependencies.
|
103
|
+
# Returns +false+ if there was a dependency that the manifest does not
|
104
|
+
# satisfy.
|
105
|
+
def check_manifest(manifest, all_deps)
|
106
|
+
related = all_deps.select{|d| d.name == manifest.name}
|
107
|
+
if q = related.find{|d| !d.satisfied_by?(manifest)}
|
108
|
+
debug_conflict manifest, q
|
109
|
+
return false
|
110
|
+
end
|
111
|
+
true
|
32
112
|
end
|
33
113
|
|
34
114
|
def default_source
|
35
|
-
MultiSource.new(spec.sources)
|
115
|
+
@default_source ||= MultiSource.new(spec.sources)
|
116
|
+
end
|
117
|
+
|
118
|
+
def dependency_source_map
|
119
|
+
@dependency_source_map ||=
|
120
|
+
Hash[spec.dependencies.map{|d| [d.name, d.source]}]
|
36
121
|
end
|
37
122
|
|
38
123
|
def sourced_dependency_for(dependency)
|
@@ -42,75 +127,81 @@ module Librarian
|
|
42
127
|
Dependency.new(dependency.name, dependency.requirement, source)
|
43
128
|
end
|
44
129
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
130
|
+
def sourced_dependencies_for_manifest(manifest)
|
131
|
+
manifest.dependencies.map{|d| sourced_dependency_for(d)}
|
132
|
+
end
|
133
|
+
|
134
|
+
def sourced_dependencies_for_manifests(manifests)
|
135
|
+
manifests = manifests.values if manifests.kind_of?(Hash)
|
136
|
+
manifests.map{|m| sourced_dependencies_for_manifest(m)}.flatten(1)
|
137
|
+
end
|
138
|
+
|
139
|
+
def resolving_dependency_map_find_manifests(dependency)
|
140
|
+
scope_resolving_dependency dependency do
|
141
|
+
map_find(dependency.manifests) do |manifest|
|
142
|
+
scope_checking_manifest dependency, manifest do
|
143
|
+
yield manifest
|
144
|
+
end
|
49
145
|
end
|
50
146
|
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
else
|
66
|
-
debug { "No known prior constraints" }
|
67
|
-
resolution = nil
|
68
|
-
related_dependencies = dependencies.select{|d| d.name == dependency.name}
|
69
|
-
unless dependency.manifests && dependency.manifests.first
|
70
|
-
debug { "No known manifests" }
|
71
|
-
else
|
72
|
-
debug { "Checking manifests" }
|
73
|
-
scope do
|
74
|
-
dependency.manifests.each do |manifest|
|
75
|
-
break if resolution
|
76
|
-
|
77
|
-
debug { "Checking #{manifest}" }
|
78
|
-
scope do
|
79
|
-
if related_dependencies.all?{|d| d.satisfied_by?(manifest)}
|
80
|
-
m = manifests.merge(dependency.name => manifest)
|
81
|
-
a = manifest.dependencies.map { |d| sourced_dependency_for(d) }
|
82
|
-
a.each do |d|
|
83
|
-
debug { "Scheduling #{d}" }
|
84
|
-
end
|
85
|
-
q = queue + a
|
86
|
-
resolution = recursive_resolve(dependencies.dup, m, q)
|
87
|
-
end
|
88
|
-
if resolution
|
89
|
-
debug { "Resolved #{dependency} at #{manifest}" }
|
90
|
-
else
|
91
|
-
debug { "Backtracking from #{manifest}" }
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
if resolution
|
97
|
-
debug { "Resolved #{dependency}" }
|
98
|
-
else
|
99
|
-
debug { "Failed to resolve #{dependency}" }
|
100
|
-
end
|
101
|
-
end
|
102
|
-
unless resolution
|
103
|
-
failure = true
|
104
|
-
else
|
105
|
-
dependencies, manifests, queue = *resolution
|
106
|
-
end
|
107
|
-
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def scope_resolving_dependency(dependency)
|
150
|
+
debug { "Resolving #{dependency}" }
|
151
|
+
resolution = nil
|
152
|
+
scope do
|
153
|
+
scope_checking_manifests do
|
154
|
+
resolution = yield
|
155
|
+
end
|
156
|
+
if resolution
|
157
|
+
debug { "Resolved #{dependency}" }
|
158
|
+
else
|
159
|
+
debug { "Failed to resolve #{dependency}" }
|
108
160
|
end
|
109
161
|
end
|
110
|
-
|
162
|
+
resolution
|
111
163
|
end
|
112
164
|
|
113
|
-
|
165
|
+
def scope_checking_manifests
|
166
|
+
debug { "Checking manifests" }
|
167
|
+
scope do
|
168
|
+
yield
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def scope_checking_manifest(dependency, manifest)
|
173
|
+
debug { "Checking #{manifest}" }
|
174
|
+
resolution = nil
|
175
|
+
scope do
|
176
|
+
resolution = yield
|
177
|
+
if resolution
|
178
|
+
debug { "Resolved #{dependency} at #{manifest}" }
|
179
|
+
else
|
180
|
+
debug { "Backtracking from #{manifest}" }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
resolution
|
184
|
+
end
|
185
|
+
|
186
|
+
def debug_schedule(dependency)
|
187
|
+
debug { "Scheduling #{dependency}" }
|
188
|
+
end
|
189
|
+
|
190
|
+
def debug_conflict(dependency, conflict)
|
191
|
+
debug { "Conflict between #{dependency} and #{conflict}" }
|
192
|
+
end
|
193
|
+
|
194
|
+
def map_find(enum)
|
195
|
+
enum.each do |obj|
|
196
|
+
res = yield(obj)
|
197
|
+
res.nil? or return res
|
198
|
+
end
|
199
|
+
nil
|
200
|
+
end
|
201
|
+
|
202
|
+
def index_by(enum)
|
203
|
+
Hash[enum.map{|obj| [yield(obj), obj]}]
|
204
|
+
end
|
114
205
|
|
115
206
|
def scope
|
116
207
|
@level += 1
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Librarian
|
2
|
+
module Source
|
3
|
+
module BasicApi
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
class << base
|
8
|
+
def lock_name(name)
|
9
|
+
def_sclass_prop(:lock_name, name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def spec_options(keys)
|
13
|
+
def_sclass_prop(:spec_options, keys)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def def_sclass_prop(name, arg)
|
19
|
+
sclass = class << self ; self ; end
|
20
|
+
sclass.module_exec do
|
21
|
+
remove_method(name)
|
22
|
+
define_method(name) { arg }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
def from_lock_options(environment, options)
|
30
|
+
new(environment, options[:remote], options.reject{|k, v| k == :remote})
|
31
|
+
end
|
32
|
+
|
33
|
+
def from_spec_args(environment, param, options)
|
34
|
+
recognized_options = spec_options
|
35
|
+
unrecognized_options = options.keys - recognized_options
|
36
|
+
unrecognized_options.empty? or raise Error,
|
37
|
+
"unrecognized options: #{unrecognized_options.join(", ")}"
|
38
|
+
|
39
|
+
new(environment, param, options)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/librarian/source/git.rb
CHANGED
@@ -2,36 +2,18 @@ require 'fileutils'
|
|
2
2
|
require 'pathname'
|
3
3
|
require 'digest'
|
4
4
|
|
5
|
+
require 'librarian/source/basic_api'
|
5
6
|
require 'librarian/source/git/repository'
|
6
7
|
require 'librarian/source/local'
|
7
8
|
|
8
9
|
module Librarian
|
9
10
|
module Source
|
10
11
|
class Git
|
11
|
-
|
12
|
+
include BasicApi
|
12
13
|
include Local
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
LOCK_NAME = 'GIT'
|
17
|
-
|
18
|
-
def lock_name
|
19
|
-
LOCK_NAME
|
20
|
-
end
|
21
|
-
|
22
|
-
def from_lock_options(environment, options)
|
23
|
-
new(environment, options[:remote], options.reject{|k, v| k == :remote})
|
24
|
-
end
|
25
|
-
|
26
|
-
def from_spec_args(environment, uri, options)
|
27
|
-
recognized_options = [:ref, :path]
|
28
|
-
unrecognized_options = options.keys - recognized_options
|
29
|
-
unrecognized_options.empty? or raise Error, "unrecognized options: #{unrecognized_options.join(", ")}"
|
30
|
-
|
31
|
-
new(environment, uri, options)
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
15
|
+
lock_name 'GIT'
|
16
|
+
spec_options [:ref, :path]
|
35
17
|
|
36
18
|
DEFAULTS = {
|
37
19
|
:ref => 'master'
|