librarian 0.0.1
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 +5 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +46 -0
- data/Rakefile +13 -0
- data/bin/librarian-chef +7 -0
- data/bin/librarian-mock +7 -0
- data/config/cucumber.yaml +1 -0
- data/features/chef/cli/install.feature +69 -0
- data/features/support/env.rb +5 -0
- data/lib/librarian.rb +191 -0
- data/lib/librarian/chef.rb +1 -0
- data/lib/librarian/chef/cli.rb +14 -0
- data/lib/librarian/chef/dsl.rb +14 -0
- data/lib/librarian/chef/extension.rb +24 -0
- data/lib/librarian/chef/manifest.rb +43 -0
- data/lib/librarian/chef/particularity.rb +9 -0
- data/lib/librarian/chef/source.rb +3 -0
- data/lib/librarian/chef/source/git.rb +14 -0
- data/lib/librarian/chef/source/local.rb +80 -0
- data/lib/librarian/chef/source/path.rb +14 -0
- data/lib/librarian/chef/source/site.rb +271 -0
- data/lib/librarian/cli.rb +76 -0
- data/lib/librarian/dependency.rb +44 -0
- data/lib/librarian/dsl.rb +76 -0
- data/lib/librarian/dsl/receiver.rb +46 -0
- data/lib/librarian/dsl/target.rb +164 -0
- data/lib/librarian/helpers.rb +13 -0
- data/lib/librarian/helpers/debug.rb +35 -0
- data/lib/librarian/lockfile.rb +31 -0
- data/lib/librarian/lockfile/compiler.rb +69 -0
- data/lib/librarian/lockfile/parser.rb +102 -0
- data/lib/librarian/manifest.rb +88 -0
- data/lib/librarian/manifest_set.rb +131 -0
- data/lib/librarian/mock.rb +1 -0
- data/lib/librarian/mock/cli.rb +14 -0
- data/lib/librarian/mock/dsl.rb +14 -0
- data/lib/librarian/mock/extension.rb +28 -0
- data/lib/librarian/mock/particularity.rb +7 -0
- data/lib/librarian/mock/source.rb +1 -0
- data/lib/librarian/mock/source/mock.rb +88 -0
- data/lib/librarian/mock/source/mock/registry.rb +79 -0
- data/lib/librarian/particularity.rb +7 -0
- data/lib/librarian/resolution.rb +36 -0
- data/lib/librarian/resolver.rb +139 -0
- data/lib/librarian/source.rb +2 -0
- data/lib/librarian/source/git.rb +91 -0
- data/lib/librarian/source/git/repository.rb +82 -0
- data/lib/librarian/source/local.rb +33 -0
- data/lib/librarian/source/path.rb +52 -0
- data/lib/librarian/spec.rb +11 -0
- data/lib/librarian/spec_change_set.rb +169 -0
- data/lib/librarian/specfile.rb +16 -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/librarian.gemspec +29 -0
- data/spec/chef/git_source_spec.rb +93 -0
- data/spec/dsl_spec.rb +167 -0
- data/spec/lockfile_spec.rb +44 -0
- data/spec/meta/requires_spec.rb +27 -0
- data/spec/resolver_spec.rb +172 -0
- data/spec/spec_change_set_spec.rb +165 -0
- metadata +172 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'librarian/helpers/debug'
|
2
|
+
|
3
|
+
require 'librarian/manifest'
|
4
|
+
require 'librarian/dependency'
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
class Lockfile
|
8
|
+
class Parser
|
9
|
+
|
10
|
+
class ManifestPlaceholder
|
11
|
+
attr_reader :source, :name, :version, :dependencies
|
12
|
+
def initialize(source, name, version, dependencies)
|
13
|
+
@source, @name, @version, @dependencies = source, name, version, dependencies
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
include Helpers::Debug
|
18
|
+
|
19
|
+
attr_reader :root_module
|
20
|
+
|
21
|
+
def initialize(root_module)
|
22
|
+
@root_module = root_module
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse(string)
|
26
|
+
string = string.dup
|
27
|
+
source_type_names_map = Hash[dsl_class.source_types.map{|t| [t[1].lock_name, t[1]]}]
|
28
|
+
source_type_names = dsl_class.source_types.map{|t| t[1].lock_name}
|
29
|
+
lines = string.split(/(\r?\n)+/).reject{|l| l =~ /^\s*$/}
|
30
|
+
sources = []
|
31
|
+
while source_type_names.include?(lines.first)
|
32
|
+
source = {}
|
33
|
+
source_type_name = lines.shift
|
34
|
+
source[:type] = source_type_names_map[source_type_name]
|
35
|
+
options = {}
|
36
|
+
while lines.first =~ /^ {2}([\w-]+):\s+(.+)$/
|
37
|
+
lines.shift
|
38
|
+
options[$1.to_sym] = $2
|
39
|
+
end
|
40
|
+
source[:options] = options
|
41
|
+
lines.shift # specs
|
42
|
+
manifests = {}
|
43
|
+
while lines.first =~ /^ {4}([\w-]+) \((.*)\)$/
|
44
|
+
lines.shift
|
45
|
+
name = $1
|
46
|
+
manifests[name] = {:version => $2, :dependencies => {}}
|
47
|
+
while lines.first =~ /^ {6}([\w-]+) \((.*)\)$/
|
48
|
+
lines.shift
|
49
|
+
manifests[name][:dependencies][$1] = $2.split(/,\s*/)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
source[:manifests] = manifests
|
53
|
+
sources << source
|
54
|
+
end
|
55
|
+
manifests = compile(sources)
|
56
|
+
manifests_index = Hash[manifests.map{|m| [m.name, m]}]
|
57
|
+
raise StandardError, "Expected DEPENDENCIES topic!" unless lines.shift == "DEPENDENCIES"
|
58
|
+
dependencies = []
|
59
|
+
while lines.first =~ /^ {2}([\w-]+)(?: \((.*)\))?$/
|
60
|
+
lines.shift
|
61
|
+
name, requirement = $1, $2
|
62
|
+
dependencies << Dependency.new(name, requirement, manifests_index[name].source)
|
63
|
+
end
|
64
|
+
Resolution.new(dependencies, manifests)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def compile(sources_ast)
|
70
|
+
manifests = {}
|
71
|
+
sources_ast.each do |source_ast|
|
72
|
+
source_type = source_ast[:type]
|
73
|
+
source = source_type.from_lock_options(source_ast[:options])
|
74
|
+
source_ast[:manifests].each do |manifest_name, manifest_ast|
|
75
|
+
manifests[manifest_name] = ManifestPlaceholder.new(
|
76
|
+
source,
|
77
|
+
manifest_name,
|
78
|
+
manifest_ast[:version],
|
79
|
+
manifest_ast[:dependencies].map{|k, v| Dependency.new(k, v, nil)}
|
80
|
+
)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
manifests = manifests.map do |name, manifest|
|
84
|
+
dependencies = manifest.dependencies.map do |d|
|
85
|
+
Dependency.new(d.name, d.requirement, manifests[d.name].source)
|
86
|
+
end
|
87
|
+
manifest.source.manifest(
|
88
|
+
manifest.name,
|
89
|
+
manifest.version,
|
90
|
+
dependencies
|
91
|
+
)
|
92
|
+
end
|
93
|
+
Resolver.new(root_module).sort(manifests)
|
94
|
+
end
|
95
|
+
|
96
|
+
def dsl_class
|
97
|
+
root_module.dsl_class
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'librarian/helpers/debug'
|
4
|
+
require 'librarian/support/abstract_method'
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
class Manifest
|
8
|
+
|
9
|
+
class Version < Gem::Version
|
10
|
+
end
|
11
|
+
|
12
|
+
include Support::AbstractMethod
|
13
|
+
include Helpers::Debug
|
14
|
+
|
15
|
+
attr_reader :source, :name
|
16
|
+
|
17
|
+
abstract_method :fetch_version!, :fetch_dependencies!
|
18
|
+
abstract_method :install!
|
19
|
+
|
20
|
+
def initialize(source, name)
|
21
|
+
@source = source
|
22
|
+
@name = name
|
23
|
+
@fetched_version = nil
|
24
|
+
@defined_version = nil
|
25
|
+
@fetched_dependencies = nil
|
26
|
+
@defined_dependencies = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
"#{name}/#{version} <#{source}>"
|
31
|
+
end
|
32
|
+
|
33
|
+
def version
|
34
|
+
@defined_version || @fetched_version ||= _normalize_version(fetch_version!)
|
35
|
+
end
|
36
|
+
|
37
|
+
def version=(version)
|
38
|
+
@defined_version = _normalize_version(version)
|
39
|
+
end
|
40
|
+
|
41
|
+
def version?
|
42
|
+
if @defined_version
|
43
|
+
@fetched_version ||= _normalize_version(fetch_version!)
|
44
|
+
@defined_version == @fetched_version
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def dependencies
|
49
|
+
@defined_dependencies || @fetched_dependencies ||= _normalize_dependencies(fetch_dependencies!)
|
50
|
+
end
|
51
|
+
|
52
|
+
def dependencies=(dependencies)
|
53
|
+
@defined_dependencies = _normalize_dependencies(dependencies)
|
54
|
+
end
|
55
|
+
|
56
|
+
def dependencies?
|
57
|
+
if @defined_dependencies
|
58
|
+
@fetched_dependencies ||= _normalize_dependencies(fetch_dependencies!)
|
59
|
+
@defined_dependencies.zip(@fetched_dependencies).all? do |pair|
|
60
|
+
a, b = *pair
|
61
|
+
a.name == b.name && a.requirement == b.requirement
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def satisfies?(dependency)
|
67
|
+
dependency.requirement.satisfied_by?(version)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def root_module
|
73
|
+
source.root_module
|
74
|
+
end
|
75
|
+
|
76
|
+
def _normalize_version(version)
|
77
|
+
Version.new(version)
|
78
|
+
end
|
79
|
+
|
80
|
+
def _normalize_dependencies(dependencies)
|
81
|
+
if Hash === dependencies
|
82
|
+
dependencies = dependencies.map{|k, v| Dependency.new(k, v, nil)}
|
83
|
+
end
|
84
|
+
dependencies.sort_by{|d| d.name}
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'tsort'
|
3
|
+
|
4
|
+
module Librarian
|
5
|
+
class ManifestSet
|
6
|
+
|
7
|
+
class GraphHash < Hash
|
8
|
+
include TSort
|
9
|
+
alias tsort_each_node each_key
|
10
|
+
def tsort_each_child(node, &block)
|
11
|
+
self[node].each(&block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def shallow_strip(manifests, names)
|
17
|
+
new(manifests).shallow_strip!(names).send(method_for(manifests))
|
18
|
+
end
|
19
|
+
def deep_strip(manifests, names)
|
20
|
+
new(manifests).deep_strip!(names).send(method_for(manifests))
|
21
|
+
end
|
22
|
+
def shallow_keep(manifests, names)
|
23
|
+
new(manifests).shallow_keep!(names).send(method_for(manifests))
|
24
|
+
end
|
25
|
+
def deep_keep(manifests, names)
|
26
|
+
new(manifests).deep_keep!(names).send(method_for(manifests))
|
27
|
+
end
|
28
|
+
def sort(manifests)
|
29
|
+
manifests = Hash[manifests.map{|m| [m.name, m]}] if Array === manifests
|
30
|
+
manifest_pairs = GraphHash[manifests.map{|k, m| [k, m.dependencies.map{|d| d.name}]}]
|
31
|
+
manifest_names = manifest_pairs.tsort
|
32
|
+
manifest_names.map{|n| manifests[n]}
|
33
|
+
end
|
34
|
+
private
|
35
|
+
def method_for(manifests)
|
36
|
+
case manifests
|
37
|
+
when Hash
|
38
|
+
:to_hash
|
39
|
+
when Array
|
40
|
+
:to_a
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(manifests)
|
46
|
+
self.index = Hash === manifests ? manifests.dup : Hash[manifests.map{|m| [m.name, m]}]
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_a
|
50
|
+
index.values
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_hash
|
54
|
+
index.dup
|
55
|
+
end
|
56
|
+
|
57
|
+
def dup
|
58
|
+
self.class.new(index)
|
59
|
+
end
|
60
|
+
|
61
|
+
def shallow_strip(names)
|
62
|
+
dup.shallow_strip!(names)
|
63
|
+
end
|
64
|
+
|
65
|
+
def shallow_strip!(names)
|
66
|
+
names.each do |name|
|
67
|
+
index.delete(name)
|
68
|
+
end
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
def deep_strip(names)
|
73
|
+
dup.deep_strip!(names)
|
74
|
+
end
|
75
|
+
|
76
|
+
def deep_strip!(names)
|
77
|
+
names = Array === names ? names.dup : names.to_a
|
78
|
+
until names.empty?
|
79
|
+
name = names.shift
|
80
|
+
manifest = index.delete(name)
|
81
|
+
manifest.dependencies.each do |dependency|
|
82
|
+
names << dependency.name
|
83
|
+
end
|
84
|
+
end
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
def shallow_keep(names)
|
89
|
+
dup.shallow_keep!(names)
|
90
|
+
end
|
91
|
+
|
92
|
+
def shallow_keep!(names)
|
93
|
+
names = Set.new(names) unless Set === names
|
94
|
+
index.reject! { |k, v| !names.include?(k) }
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def deep_keep(names)
|
99
|
+
dup.conservative_strip!(names)
|
100
|
+
end
|
101
|
+
|
102
|
+
def deep_keep!(names)
|
103
|
+
names = Array === names ? names.dup : names.to_a
|
104
|
+
marks = Set.new
|
105
|
+
until names.empty?
|
106
|
+
keep = names.shift
|
107
|
+
unless marks.include?(keep)
|
108
|
+
marks << keep
|
109
|
+
index[keep].dependencies.each do |d|
|
110
|
+
names << d.name
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
shallow_keep!(marks)
|
115
|
+
end
|
116
|
+
|
117
|
+
def consistent?
|
118
|
+
index.values.all? do |manifest|
|
119
|
+
manifest.dependencies.all? do |dependency|
|
120
|
+
match = index[dependency.name]
|
121
|
+
match && match.satisfies?(dependency)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
attr_accessor :index
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'librarian/mock/extension'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'librarian/specfile'
|
2
|
+
require 'librarian/mock/dsl'
|
3
|
+
|
4
|
+
module Librarian
|
5
|
+
module Mock
|
6
|
+
extend self
|
7
|
+
extend Librarian
|
8
|
+
|
9
|
+
module Overrides
|
10
|
+
def specfile_name
|
11
|
+
'Mockfile'
|
12
|
+
end
|
13
|
+
|
14
|
+
def install_path
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def registry(options = nil, &block)
|
19
|
+
registry = Source::Mock::Registry
|
20
|
+
registry.clear! if options && options[:clear]
|
21
|
+
registry.merge!(&block) if block
|
22
|
+
registry
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
extend Overrides
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'librarian/mock/source/mock'
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'librarian/manifest'
|
2
|
+
require 'librarian/mock/particularity'
|
3
|
+
require 'librarian/mock/source/mock/registry'
|
4
|
+
|
5
|
+
module Librarian
|
6
|
+
module Mock
|
7
|
+
module Source
|
8
|
+
class Mock
|
9
|
+
|
10
|
+
class Manifest < Manifest
|
11
|
+
attr_reader :manifest
|
12
|
+
def initialize(source, name, manifest)
|
13
|
+
super(source, name)
|
14
|
+
@manifest = manifest
|
15
|
+
end
|
16
|
+
def fetch_version!
|
17
|
+
manifest[:version]
|
18
|
+
end
|
19
|
+
def fetch_dependencies!
|
20
|
+
manifest[:dependencies]
|
21
|
+
end
|
22
|
+
def install!
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
include Particularity
|
27
|
+
|
28
|
+
class << self
|
29
|
+
LOCK_NAME = 'MOCK'
|
30
|
+
def lock_name
|
31
|
+
LOCK_NAME
|
32
|
+
end
|
33
|
+
def from_lock_options(options)
|
34
|
+
new(options[:remote], options.reject{|k, v| k == :remote})
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :name
|
39
|
+
|
40
|
+
def initialize(name, options)
|
41
|
+
@name = name
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
name
|
46
|
+
end
|
47
|
+
|
48
|
+
def ==(other)
|
49
|
+
other &&
|
50
|
+
self.class == other.class &&
|
51
|
+
self.name == other.name
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_spec_args
|
55
|
+
[name, {}]
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_lock_options
|
59
|
+
{:remote => name}
|
60
|
+
end
|
61
|
+
|
62
|
+
def registry
|
63
|
+
Registry[name]
|
64
|
+
end
|
65
|
+
|
66
|
+
def manifest(name, version, dependencies)
|
67
|
+
Manifest.new(self, name, {:version => version, :dependencies => dependencies})
|
68
|
+
end
|
69
|
+
|
70
|
+
def manifests(dependency)
|
71
|
+
if d = registry[dependency.name]
|
72
|
+
d.map{|v| Manifest.new(self, dependency.name, v)}
|
73
|
+
else
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def cache!(dependencies)
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_s
|
82
|
+
name
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|