librarianp 0.1.2
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 +7 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +255 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +235 -0
- data/LICENSE.txt +22 -0
- data/README.md +55 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/lib/librarian/action/base.rb +24 -0
- data/lib/librarian/action/clean.rb +44 -0
- data/lib/librarian/action/ensure.rb +24 -0
- data/lib/librarian/action/install.rb +95 -0
- data/lib/librarian/action/persist_resolution_mixin.rb +51 -0
- data/lib/librarian/action/resolve.rb +46 -0
- data/lib/librarian/action/update.rb +44 -0
- data/lib/librarian/action.rb +5 -0
- data/lib/librarian/algorithms.rb +133 -0
- data/lib/librarian/cli/manifest_presenter.rb +89 -0
- data/lib/librarian/cli.rb +225 -0
- data/lib/librarian/config/database.rb +205 -0
- data/lib/librarian/config/file_source.rb +47 -0
- data/lib/librarian/config/hash_source.rb +33 -0
- data/lib/librarian/config/source.rb +149 -0
- data/lib/librarian/config.rb +7 -0
- data/lib/librarian/dependency.rb +153 -0
- data/lib/librarian/dsl/receiver.rb +42 -0
- data/lib/librarian/dsl/target.rb +171 -0
- data/lib/librarian/dsl.rb +102 -0
- data/lib/librarian/environment/runtime_cache.rb +101 -0
- data/lib/librarian/environment.rb +230 -0
- data/lib/librarian/error.rb +4 -0
- data/lib/librarian/helpers.rb +29 -0
- data/lib/librarian/linter/source_linter.rb +55 -0
- data/lib/librarian/lockfile/compiler.rb +66 -0
- data/lib/librarian/lockfile/parser.rb +123 -0
- data/lib/librarian/lockfile.rb +29 -0
- data/lib/librarian/logger.rb +46 -0
- data/lib/librarian/manifest.rb +146 -0
- data/lib/librarian/manifest_set.rb +150 -0
- data/lib/librarian/mock/cli.rb +19 -0
- data/lib/librarian/mock/dsl.rb +15 -0
- data/lib/librarian/mock/environment.rb +21 -0
- data/lib/librarian/mock/extension.rb +9 -0
- data/lib/librarian/mock/source/mock/registry.rb +83 -0
- data/lib/librarian/mock/source/mock.rb +80 -0
- data/lib/librarian/mock/source.rb +1 -0
- data/lib/librarian/mock/version.rb +5 -0
- data/lib/librarian/mock.rb +1 -0
- data/lib/librarian/posix.rb +129 -0
- data/lib/librarian/resolution.rb +46 -0
- data/lib/librarian/resolver/implementation.rb +238 -0
- data/lib/librarian/resolver.rb +94 -0
- data/lib/librarian/rspec/support/cli_macro.rb +120 -0
- data/lib/librarian/source/basic_api.rb +45 -0
- data/lib/librarian/source/git/repository.rb +193 -0
- data/lib/librarian/source/git.rb +172 -0
- data/lib/librarian/source/local.rb +54 -0
- data/lib/librarian/source/path.rb +56 -0
- data/lib/librarian/source.rb +2 -0
- data/lib/librarian/spec.rb +13 -0
- data/lib/librarian/spec_change_set.rb +173 -0
- data/lib/librarian/specfile.rb +19 -0
- data/lib/librarian/support/abstract_method.rb +21 -0
- data/lib/librarian/ui.rb +64 -0
- data/lib/librarian/version.rb +3 -0
- data/lib/librarian.rb +11 -0
- data/librarian.gemspec +47 -0
- data/spec/functional/cli_spec.rb +27 -0
- data/spec/functional/posix_spec.rb +32 -0
- data/spec/functional/source/git/repository_spec.rb +199 -0
- data/spec/functional/source/git_spec.rb +174 -0
- data/spec/support/fakefs.rb +37 -0
- data/spec/support/method_patch_macro.rb +30 -0
- data/spec/support/project_path_macro.rb +14 -0
- data/spec/support/with_env_macro.rb +22 -0
- data/spec/unit/action/base_spec.rb +18 -0
- data/spec/unit/action/clean_spec.rb +102 -0
- data/spec/unit/action/ensure_spec.rb +37 -0
- data/spec/unit/action/install_spec.rb +111 -0
- data/spec/unit/algorithms_spec.rb +131 -0
- data/spec/unit/config/database_spec.rb +320 -0
- data/spec/unit/dependency/requirement_spec.rb +12 -0
- data/spec/unit/dependency_spec.rb +212 -0
- data/spec/unit/dsl_spec.rb +173 -0
- data/spec/unit/environment/runtime_cache_spec.rb +73 -0
- data/spec/unit/environment_spec.rb +209 -0
- data/spec/unit/lockfile/parser_spec.rb +162 -0
- data/spec/unit/lockfile_spec.rb +65 -0
- data/spec/unit/manifest/version_spec.rb +11 -0
- data/spec/unit/manifest_set_spec.rb +202 -0
- data/spec/unit/manifest_spec.rb +36 -0
- data/spec/unit/mock/environment_spec.rb +25 -0
- data/spec/unit/mock/source/mock_spec.rb +22 -0
- data/spec/unit/resolver_spec.rb +299 -0
- data/spec/unit/source/git_spec.rb +29 -0
- data/spec/unit/spec_change_set_spec.rb +169 -0
- metadata +257 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require "librarian/action/base"
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
module Action
|
5
|
+
class Clean < Base
|
6
|
+
|
7
|
+
def run
|
8
|
+
clean_cache_path
|
9
|
+
clean_install_path
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def clean_cache_path
|
15
|
+
if cache_path.exist?
|
16
|
+
debug { "Deleting #{project_relative_path_to(cache_path)}" }
|
17
|
+
cache_path.rmtree
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def clean_install_path
|
22
|
+
if install_path.exist?
|
23
|
+
install_path.children.each do |c|
|
24
|
+
debug { "Deleting #{project_relative_path_to(c)}" }
|
25
|
+
c.rmtree unless c.file?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def cache_path
|
31
|
+
environment.cache_path
|
32
|
+
end
|
33
|
+
|
34
|
+
def install_path
|
35
|
+
environment.install_path
|
36
|
+
end
|
37
|
+
|
38
|
+
def project_relative_path_to(path)
|
39
|
+
environment.project_relative_path_to(path)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "librarian/error"
|
2
|
+
require "librarian/action/base"
|
3
|
+
|
4
|
+
module Librarian
|
5
|
+
module Action
|
6
|
+
class Ensure < Base
|
7
|
+
|
8
|
+
def run
|
9
|
+
raise Error, "Cannot find #{specfile_name}!" unless project_path
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def specfile_name
|
15
|
+
environment.specfile_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def project_path
|
19
|
+
environment.project_path
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require "librarian/manifest_set"
|
2
|
+
require "librarian/spec_change_set"
|
3
|
+
require "librarian/action/base"
|
4
|
+
|
5
|
+
module Librarian
|
6
|
+
module Action
|
7
|
+
class Install < Base
|
8
|
+
|
9
|
+
def run
|
10
|
+
check_preconditions
|
11
|
+
|
12
|
+
perform_installation
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def check_preconditions
|
18
|
+
check_specfile
|
19
|
+
check_lockfile
|
20
|
+
check_consistent
|
21
|
+
end
|
22
|
+
|
23
|
+
def check_specfile
|
24
|
+
raise Error, "#{specfile_name} missing!" unless specfile_path.exist?
|
25
|
+
end
|
26
|
+
|
27
|
+
def check_lockfile
|
28
|
+
raise Error, "#{lockfile_name} missing!" unless lockfile_path.exist?
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_consistent
|
32
|
+
raise Error, "#{specfile_name} and #{lockfile_name} are out of sync!" unless spec_consistent_with_lock?
|
33
|
+
end
|
34
|
+
|
35
|
+
def perform_installation
|
36
|
+
manifests = sorted_manifests
|
37
|
+
|
38
|
+
create_install_path
|
39
|
+
install_manifests(manifests)
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_install_path
|
43
|
+
install_path.rmtree if install_path.exist?
|
44
|
+
install_path.mkpath
|
45
|
+
end
|
46
|
+
|
47
|
+
def install_manifests(manifests)
|
48
|
+
manifests.each do |manifest|
|
49
|
+
manifest.install!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def sorted_manifests
|
54
|
+
ManifestSet.sort(lock.manifests)
|
55
|
+
end
|
56
|
+
|
57
|
+
def specfile_name
|
58
|
+
environment.specfile_name
|
59
|
+
end
|
60
|
+
|
61
|
+
def specfile_path
|
62
|
+
environment.specfile_path
|
63
|
+
end
|
64
|
+
|
65
|
+
def lockfile_name
|
66
|
+
environment.lockfile_name
|
67
|
+
end
|
68
|
+
|
69
|
+
def lockfile_path
|
70
|
+
environment.lockfile_path
|
71
|
+
end
|
72
|
+
|
73
|
+
def spec
|
74
|
+
environment.spec
|
75
|
+
end
|
76
|
+
|
77
|
+
def lock
|
78
|
+
environment.lock
|
79
|
+
end
|
80
|
+
|
81
|
+
def spec_change_set(spec, lock)
|
82
|
+
SpecChangeSet.new(environment, spec, lock)
|
83
|
+
end
|
84
|
+
|
85
|
+
def spec_consistent_with_lock?
|
86
|
+
spec_change_set(spec, lock).same?
|
87
|
+
end
|
88
|
+
|
89
|
+
def install_path
|
90
|
+
environment.install_path
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "librarian/error"
|
2
|
+
require "librarian/spec_change_set"
|
3
|
+
|
4
|
+
module Librarian
|
5
|
+
module Action
|
6
|
+
module PersistResolutionMixin
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def persist_resolution(resolution)
|
11
|
+
resolution && resolution.correct? or raise Error,
|
12
|
+
"Could not resolve the dependencies."
|
13
|
+
|
14
|
+
lockfile_text = lockfile.save(resolution)
|
15
|
+
debug { "Bouncing #{lockfile_name}" }
|
16
|
+
bounced_lockfile_text = lockfile.save(lockfile.load(lockfile_text))
|
17
|
+
unless bounced_lockfile_text == lockfile_text
|
18
|
+
debug { "lockfile_text: \n#{lockfile_text}" }
|
19
|
+
debug { "bounced_lockfile_text: \n#{bounced_lockfile_text}" }
|
20
|
+
raise Error, "Cannot bounce #{lockfile_name}!"
|
21
|
+
end
|
22
|
+
lockfile_path.open('wb') { |f| f.write(lockfile_text) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def specfile_name
|
26
|
+
environment.specfile_name
|
27
|
+
end
|
28
|
+
|
29
|
+
def lockfile_name
|
30
|
+
environment.lockfile_name
|
31
|
+
end
|
32
|
+
|
33
|
+
def specfile_path
|
34
|
+
environment.specfile_path
|
35
|
+
end
|
36
|
+
|
37
|
+
def lockfile_path
|
38
|
+
environment.lockfile_path
|
39
|
+
end
|
40
|
+
|
41
|
+
def specfile
|
42
|
+
environment.specfile
|
43
|
+
end
|
44
|
+
|
45
|
+
def lockfile
|
46
|
+
environment.lockfile
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "librarian/resolver"
|
2
|
+
require "librarian/spec_change_set"
|
3
|
+
require "librarian/action/base"
|
4
|
+
require "librarian/action/persist_resolution_mixin"
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
module Action
|
8
|
+
class Resolve < Base
|
9
|
+
include PersistResolutionMixin
|
10
|
+
|
11
|
+
def run
|
12
|
+
if force? || !lockfile_path.exist?
|
13
|
+
spec = specfile.read
|
14
|
+
manifests = []
|
15
|
+
else
|
16
|
+
lock = lockfile.read
|
17
|
+
spec = specfile.read(lock.sources)
|
18
|
+
changes = spec_change_set(spec, lock)
|
19
|
+
if changes.same?
|
20
|
+
debug { "The specfile is unchanged: nothing to do." }
|
21
|
+
return
|
22
|
+
end
|
23
|
+
manifests = changes.analyze
|
24
|
+
end
|
25
|
+
|
26
|
+
resolution = resolver.resolve(spec, manifests)
|
27
|
+
persist_resolution(resolution)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def force?
|
33
|
+
options[:force]
|
34
|
+
end
|
35
|
+
|
36
|
+
def resolver
|
37
|
+
Resolver.new(environment)
|
38
|
+
end
|
39
|
+
|
40
|
+
def spec_change_set(spec, lock)
|
41
|
+
SpecChangeSet.new(environment, spec, lock)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "librarian/manifest_set"
|
2
|
+
require "librarian/resolver"
|
3
|
+
require "librarian/spec_change_set"
|
4
|
+
require "librarian/action/base"
|
5
|
+
require "librarian/action/persist_resolution_mixin"
|
6
|
+
|
7
|
+
module Librarian
|
8
|
+
module Action
|
9
|
+
class Update < Base
|
10
|
+
include PersistResolutionMixin
|
11
|
+
|
12
|
+
def run
|
13
|
+
unless lockfile_path.exist?
|
14
|
+
raise Error, "Lockfile missing!"
|
15
|
+
end
|
16
|
+
previous_resolution = lockfile.load(lockfile_path.read)
|
17
|
+
spec = specfile.read(previous_resolution.sources)
|
18
|
+
changes = spec_change_set(spec, previous_resolution)
|
19
|
+
manifests = changes.same? ? previous_resolution.manifests : changes.analyze
|
20
|
+
partial_manifests = ManifestSet.deep_strip(manifests, dependency_names)
|
21
|
+
unpinnable_sources = previous_resolution.sources - partial_manifests.map(&:source)
|
22
|
+
unpinnable_sources.each(&:unpin!)
|
23
|
+
|
24
|
+
resolution = resolver.resolve(spec, partial_manifests)
|
25
|
+
persist_resolution(resolution)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def dependency_names
|
31
|
+
options[:names]
|
32
|
+
end
|
33
|
+
|
34
|
+
def resolver
|
35
|
+
Resolver.new(environment)
|
36
|
+
end
|
37
|
+
|
38
|
+
def spec_change_set(spec, lock)
|
39
|
+
SpecChangeSet.new(environment, spec, lock)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require "set"
|
2
|
+
require "tsort"
|
3
|
+
|
4
|
+
module Librarian
|
5
|
+
module Algorithms
|
6
|
+
|
7
|
+
class GraphHash < Hash
|
8
|
+
include TSort
|
9
|
+
def tsort_each_node(&block)
|
10
|
+
keys.sort.each(&block) # demand determinism
|
11
|
+
end
|
12
|
+
def tsort_each_child(node, &block)
|
13
|
+
children = self[node]
|
14
|
+
children && children.sort.each(&block) # demand determinism
|
15
|
+
end
|
16
|
+
class << self
|
17
|
+
def from(hash)
|
18
|
+
o = new
|
19
|
+
hash.each{|k, v| o[k] = v}
|
20
|
+
o
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module AdjacencyListDirectedGraph
|
26
|
+
extend self
|
27
|
+
|
28
|
+
def cyclic?(graph)
|
29
|
+
each_cyclic_strongly_connected_component_set(graph).any?
|
30
|
+
end
|
31
|
+
|
32
|
+
# Topological sort of the graph with an approximately minimal feedback arc
|
33
|
+
# set removed.
|
34
|
+
def tsort_cyclic(graph)
|
35
|
+
fag = feedback_arc_graph(graph)
|
36
|
+
reduced_graph = subtract_edges_graph(graph, fag)
|
37
|
+
GraphHash.from(reduced_graph).tsort
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns an approximately minimal feedback arc set, lifted into a graph.
|
41
|
+
def feedback_arc_graph(graph)
|
42
|
+
edges_to_graph(feedback_arc_set(graph))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns an approximately minimal feedback arc set.
|
46
|
+
def feedback_arc_set(graph)
|
47
|
+
fas = feedback_arc_set_step0(graph)
|
48
|
+
feedback_arc_set_step1(graph, fas)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def edges_to_graph(edges)
|
54
|
+
graph = {}
|
55
|
+
edges.each do |(u, v)|
|
56
|
+
graph[u] ||= []
|
57
|
+
graph[u] << v
|
58
|
+
graph[v] ||= nil
|
59
|
+
end
|
60
|
+
graph
|
61
|
+
end
|
62
|
+
|
63
|
+
def subtract_edges_graph(graph, edges_graph)
|
64
|
+
xgraph = {}
|
65
|
+
graph.each do |n, vs|
|
66
|
+
dests = edges_graph[n]
|
67
|
+
xgraph[n] = !vs ? vs : !dests ? vs : vs - dests
|
68
|
+
end
|
69
|
+
xgraph
|
70
|
+
end
|
71
|
+
|
72
|
+
def each_cyclic_strongly_connected_component_set(graph)
|
73
|
+
return enum_for(__method__, graph) unless block_given?
|
74
|
+
GraphHash.from(graph).each_strongly_connected_component do |scc|
|
75
|
+
if scc.size == 1
|
76
|
+
n = scc.first
|
77
|
+
vs = graph[n] or next
|
78
|
+
vs.include?(n) or next
|
79
|
+
end
|
80
|
+
yield scc
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Partitions the graph into its strongly connected component subgraphs,
|
85
|
+
# removes the acyclic single-vertex components (multi-vertex components
|
86
|
+
# are guaranteed to be cyclic), and yields each cyclic strongly connected
|
87
|
+
# component.
|
88
|
+
def each_cyclic_strongly_connected_component_graph(graph)
|
89
|
+
return enum_for(__method__, graph) unless block_given?
|
90
|
+
each_cyclic_strongly_connected_component_set(graph) do |scc|
|
91
|
+
sccs = scc.to_set
|
92
|
+
sccg = GraphHash.new
|
93
|
+
scc.each do |n|
|
94
|
+
vs = graph[n]
|
95
|
+
sccg[n] = vs && vs.select{|v| sccs.include?(v)}
|
96
|
+
end
|
97
|
+
yield sccg
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# The 0th step of computing a feedback arc set: pick out vertices whose
|
102
|
+
# removals will make the graph acyclic.
|
103
|
+
def feedback_arc_set_step0(graph)
|
104
|
+
fas = []
|
105
|
+
each_cyclic_strongly_connected_component_graph(graph) do |scc|
|
106
|
+
scc.keys.sort.each do |n| # demand determinism
|
107
|
+
vs = scc[n] or next
|
108
|
+
vs.size > 0 or next
|
109
|
+
vs.sort! # demand determinism
|
110
|
+
fas << [n, vs.shift]
|
111
|
+
break
|
112
|
+
end
|
113
|
+
end
|
114
|
+
fas
|
115
|
+
end
|
116
|
+
|
117
|
+
# The 1st step of computing a feedback arc set: pick out vertices from the
|
118
|
+
# 0th step whose removals turn out to be unnecessary.
|
119
|
+
def feedback_arc_set_step1(graph, fas)
|
120
|
+
reduced_graph = subtract_edges_graph(graph, edges_to_graph(fas))
|
121
|
+
fas.select do |(u, v)|
|
122
|
+
reduced_graph[u] ||= []
|
123
|
+
reduced_graph[u] << v
|
124
|
+
cyclic = cyclic?(reduced_graph)
|
125
|
+
reduced_graph[u].pop if cyclic
|
126
|
+
cyclic
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Librarian
|
2
|
+
class Cli
|
3
|
+
class ManifestPresenter
|
4
|
+
|
5
|
+
attr_accessor :cli, :manifests
|
6
|
+
private :cli=, :manifests=
|
7
|
+
|
8
|
+
def initialize(cli, manifests)
|
9
|
+
self.cli = cli or raise ArgumentError, "cli required"
|
10
|
+
self.manifests = manifests or raise ArgumentError, "manifests required"
|
11
|
+
self.manifests_index = Hash[manifests.map{|m| [m.name, m]}]
|
12
|
+
|
13
|
+
self.scope_level = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def present(names = [], options = { })
|
17
|
+
full = options[:detailed]
|
18
|
+
full = !names.empty? if full.nil?
|
19
|
+
|
20
|
+
names = manifests.map(&:name).sort if names.empty?
|
21
|
+
assert_no_manifests_missing!(names)
|
22
|
+
|
23
|
+
present_each(names, :detailed => full)
|
24
|
+
end
|
25
|
+
|
26
|
+
def present_one(manifest, options = { })
|
27
|
+
full = options[:detailed]
|
28
|
+
|
29
|
+
say "#{manifest.name} (#{manifest.version})" do
|
30
|
+
full or next
|
31
|
+
|
32
|
+
present_one_source(manifest)
|
33
|
+
present_one_dependencies(manifest)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def present_each(names, options)
|
40
|
+
names.each do |name|
|
41
|
+
manifest = manifest(name)
|
42
|
+
present_one(manifest, options)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def present_one_source(manifest)
|
47
|
+
say "source: #{manifest.source}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def present_one_dependencies(manifest)
|
51
|
+
manifest.dependencies.empty? and return
|
52
|
+
|
53
|
+
say "dependencies:" do
|
54
|
+
deps = manifest.dependencies.sort_by(&:name)
|
55
|
+
deps.each do |dependency|
|
56
|
+
say "#{dependency.name} (#{dependency.requirement})"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
attr_accessor :scope_level, :manifests_index
|
62
|
+
|
63
|
+
def manifest(name)
|
64
|
+
manifests_index[name]
|
65
|
+
end
|
66
|
+
|
67
|
+
def say(string)
|
68
|
+
cli.say " " * scope_level << string
|
69
|
+
scope { yield } if block_given?
|
70
|
+
end
|
71
|
+
|
72
|
+
def scope
|
73
|
+
original_scope_level = scope_level
|
74
|
+
self.scope_level = scope_level + 1
|
75
|
+
yield
|
76
|
+
ensure
|
77
|
+
self.scope_level = original_scope_level
|
78
|
+
end
|
79
|
+
|
80
|
+
def assert_no_manifests_missing!(names)
|
81
|
+
missing_names = names.reject{|name| manifest(name)}
|
82
|
+
unless missing_names.empty?
|
83
|
+
raise Error, "not found: #{missing_names.map(&:inspect).join(', ')}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|