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,44 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
class Dependency
|
5
|
+
|
6
|
+
class Requirement < Gem::Requirement
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :name, :requirement, :source
|
10
|
+
|
11
|
+
def initialize(name, requirement, source)
|
12
|
+
@name = name
|
13
|
+
@requirement = Requirement.create(requirement)
|
14
|
+
@source = source
|
15
|
+
@manifests = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def manifests
|
19
|
+
@manifests ||= cache_manifests!
|
20
|
+
end
|
21
|
+
|
22
|
+
def cache_manifests!
|
23
|
+
source.cache!([self])
|
24
|
+
source.manifests(self)
|
25
|
+
end
|
26
|
+
|
27
|
+
def satisfied_by?(manifest)
|
28
|
+
manifest.satisfies?(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
"#{name} (#{requirement}) <#{source}>"
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(other)
|
36
|
+
!other.nil? &&
|
37
|
+
self.class == other.class &&
|
38
|
+
self.name == other.name &&
|
39
|
+
self.requirement == other.requirement &&
|
40
|
+
self.source == other.source
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'librarian/dependency'
|
2
|
+
require 'librarian/dsl/receiver'
|
3
|
+
require 'librarian/dsl/target'
|
4
|
+
|
5
|
+
module Librarian
|
6
|
+
class Dsl
|
7
|
+
|
8
|
+
class Error < Exception
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def run(specfile = nil, precache_sources = [], &block)
|
14
|
+
new.run(specfile, precache_sources, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def dependency(name)
|
20
|
+
dependency_name = name
|
21
|
+
dependency_type = Dependency
|
22
|
+
singleton_class = class << self; self end
|
23
|
+
singleton_class.instance_eval do
|
24
|
+
define_method(:dependency_name) { dependency_name }
|
25
|
+
define_method(:dependency_type) { dependency_type }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
define_method(:source_types) { [] }
|
30
|
+
|
31
|
+
def source(options)
|
32
|
+
name = options.keys.first
|
33
|
+
type = options[name]
|
34
|
+
types = source_types
|
35
|
+
types << [name, type]
|
36
|
+
singleton_class = class << self; self end
|
37
|
+
singleton_class.instance_eval do
|
38
|
+
define_method(:source_types) { types }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
define_method(:source_shortcuts) { {} }
|
43
|
+
|
44
|
+
def shortcut(name, options)
|
45
|
+
instances = source_shortcuts
|
46
|
+
instances[name] = options
|
47
|
+
singleton_class = class << self; self end
|
48
|
+
singleton_class.instance_eval do
|
49
|
+
define_method(:source_shortcuts) { instances }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def delegate_to_class(*names)
|
54
|
+
names.each do |name|
|
55
|
+
define_method(name) { self.class.send(name) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
delegate_to_class :dependency_name, :dependency_type, :source_types, :source_shortcuts
|
62
|
+
|
63
|
+
def run(specfile = nil, sources = [])
|
64
|
+
Target.new(self).tap do |target|
|
65
|
+
target.precache_sources(sources)
|
66
|
+
receiver = Receiver.new(target)
|
67
|
+
if block_given?
|
68
|
+
receiver.run(&Proc.new)
|
69
|
+
else
|
70
|
+
receiver.run(specfile)
|
71
|
+
end
|
72
|
+
end.to_spec
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'librarian/specfile'
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
class Dsl
|
5
|
+
class Receiver
|
6
|
+
|
7
|
+
def initialize(target)
|
8
|
+
singleton_class = class << self; self end
|
9
|
+
singleton_class.class_eval do
|
10
|
+
define_method(target.dependency_name) do |*args, &block|
|
11
|
+
target.dependency(*args, &block)
|
12
|
+
end
|
13
|
+
define_method(:source) do |*args, &block|
|
14
|
+
target.source(*args, &block)
|
15
|
+
end
|
16
|
+
target.source_types.each do |source_type|
|
17
|
+
name = source_type[0]
|
18
|
+
define_method(name) do |*args, &block|
|
19
|
+
target.source(name, *args, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def run(specfile = nil)
|
26
|
+
if block_given?
|
27
|
+
instance_eval(&Proc.new)
|
28
|
+
else
|
29
|
+
case specfile
|
30
|
+
when Specfile
|
31
|
+
eval(specfile.path.read, instance_binding, specfile.path.to_s, 1)
|
32
|
+
when String
|
33
|
+
eval(specfile, instance_binding)
|
34
|
+
when Proc
|
35
|
+
instance_eval(&specfile)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def instance_binding
|
41
|
+
binding
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'librarian/spec'
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
class Dsl
|
5
|
+
class Target
|
6
|
+
|
7
|
+
class SourceShortcutDefinitionReceiver
|
8
|
+
def initialize(target)
|
9
|
+
singleton_class = class << self; self end
|
10
|
+
singleton_class.class_eval do
|
11
|
+
define_method(:source) do |options|
|
12
|
+
target.source_from_options(options)
|
13
|
+
end
|
14
|
+
target.source_types.each do |source_type|
|
15
|
+
name = source_type[0]
|
16
|
+
define_method(name) do |*args|
|
17
|
+
args.push({}) unless Hash === args.last
|
18
|
+
target.source_from_params(name, *args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
SCOPABLES = [:sources]
|
26
|
+
|
27
|
+
attr_reader :dependency_name, :dependency_type
|
28
|
+
attr_reader :source_types, :source_types_map, :source_types_reverse_map, :source_type_names, :source_shortcuts
|
29
|
+
attr_reader :dependencies, :source_cache, *SCOPABLES
|
30
|
+
|
31
|
+
def initialize(dsl)
|
32
|
+
@dependency_name = dsl.dependency_name
|
33
|
+
@dependency_type = dsl.dependency_type
|
34
|
+
@source_types = dsl.source_types
|
35
|
+
@source_types_map = Hash[source_types]
|
36
|
+
@source_types_reverse_map = Hash[source_types.map{|pair| a, b = pair ; [b, a]}]
|
37
|
+
@source_type_names = source_types.map{|t| t[0]}
|
38
|
+
@source_cache = {}
|
39
|
+
@source_shortcuts = {}
|
40
|
+
@dependencies = []
|
41
|
+
SCOPABLES.each do |scopable|
|
42
|
+
instance_variable_set(:"@#{scopable}", [])
|
43
|
+
end
|
44
|
+
dsl.source_shortcuts.each do |name, param|
|
45
|
+
define_source_shortcut(name, param)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_spec
|
50
|
+
Spec.new(@sources.first, @dependencies)
|
51
|
+
end
|
52
|
+
|
53
|
+
def dependency(name, *args)
|
54
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
55
|
+
source = source_from_options(options) || @sources.last
|
56
|
+
unless source
|
57
|
+
raise Error, "#{dependency_name} #{name} is specified without a source!"
|
58
|
+
end
|
59
|
+
dep = dependency_type.new(name, args, source)
|
60
|
+
@dependencies << dep
|
61
|
+
end
|
62
|
+
|
63
|
+
def source(name, param = nil, options = nil, &block)
|
64
|
+
if !(Hash === name) && [Array, Hash, Proc].any?{|c| c === param} && !options && !block
|
65
|
+
define_source_shortcut(name, param)
|
66
|
+
elsif !(Hash === name) && !param && !options
|
67
|
+
source = source_shortcuts[name]
|
68
|
+
scope_or_directive(block) do
|
69
|
+
@sources = @sources.dup << source
|
70
|
+
end
|
71
|
+
else
|
72
|
+
name, param, options = *normalize_source_options(name, param, options || {})
|
73
|
+
source = source_from_params(name, param, options)
|
74
|
+
scope_or_directive(block) do
|
75
|
+
@sources = @sources.dup << source
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def precache_sources(sources)
|
81
|
+
sources.each do |source|
|
82
|
+
key = [source_types_reverse_map[source.class], *source.to_spec_args]
|
83
|
+
source_cache[key] = source
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def scope
|
88
|
+
currents = { }
|
89
|
+
SCOPABLES.each do |scopable|
|
90
|
+
currents[scopable] = instance_variable_get(:"@#{scopable}").dup
|
91
|
+
end
|
92
|
+
yield
|
93
|
+
ensure
|
94
|
+
SCOPABLES.reverse.each do |scopable|
|
95
|
+
instance_variable_set(:"@#{scopable}", currents[scopable])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def scope_or_directive(scoped_block = nil)
|
100
|
+
unless scoped_block
|
101
|
+
yield
|
102
|
+
else
|
103
|
+
scope do
|
104
|
+
yield
|
105
|
+
scoped_block.call
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def normalize_source_options(name, param, options)
|
111
|
+
if name.is_a?(Hash)
|
112
|
+
extract_source_parts(name)
|
113
|
+
else
|
114
|
+
[name, param, options]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def extract_source_parts(options)
|
119
|
+
if name = source_type_names.find{|name| options.key?(name)}
|
120
|
+
options = options.dup
|
121
|
+
param = options.delete(name)
|
122
|
+
[name, param, options]
|
123
|
+
else
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def source_from_options(options)
|
129
|
+
if options[:source]
|
130
|
+
source_shortcuts[options[:source]]
|
131
|
+
elsif source_parts = extract_source_parts(options)
|
132
|
+
source_from_params(*source_parts)
|
133
|
+
else
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def source_from_params(name, param, options)
|
139
|
+
source_cache[[name, param, options]] ||= begin
|
140
|
+
type = source_types_map[name]
|
141
|
+
type.new(param, options)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def source_from_source_shortcut_definition(definition)
|
146
|
+
case definition
|
147
|
+
when Array
|
148
|
+
source_from_params(*definition)
|
149
|
+
when Hash
|
150
|
+
source_from_options(definition)
|
151
|
+
when Proc
|
152
|
+
receiver = SourceShortcutDefinitionReceiver.new(self)
|
153
|
+
receiver.instance_eval(&definition)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def define_source_shortcut(name, definition)
|
158
|
+
source = source_from_source_shortcut_definition(definition)
|
159
|
+
source_shortcuts[name] = source
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Librarian
|
2
|
+
module Helpers
|
3
|
+
extend self
|
4
|
+
|
5
|
+
# [active_support/core_ext/string/strip]
|
6
|
+
def strip_heredoc(string)
|
7
|
+
indent = string.scan(/^[ \t]*(?=\S)/).min
|
8
|
+
indent = indent.respond_to?(:size) ? indent.size : 0
|
9
|
+
string.gsub(/^[ \t]{#{indent}}/, '')
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'librarian/support/abstract_method'
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
module Helpers
|
5
|
+
module Debug
|
6
|
+
|
7
|
+
include Support::AbstractMethod
|
8
|
+
|
9
|
+
LIBRARIAN_PATH = Pathname.new('../../../../').expand_path(__FILE__)
|
10
|
+
|
11
|
+
abstract_method :root_module
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def relative_path_to(path)
|
16
|
+
root_module.project_relative_path_to(path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def debug
|
20
|
+
if root_module.ui
|
21
|
+
if root_module.ui.respond_to? :debug_line_numbers and root_module.ui.debug_line_numbers
|
22
|
+
loc = caller.find{|l| !(l =~ /in `debug'$/)}
|
23
|
+
if loc =~ /^(.+):(\d+):in `(.+)'$/
|
24
|
+
loc = "#{Pathname.new($1).relative_path_from(LIBRARIAN_PATH)}:#{$2}:in `#{$3}'"
|
25
|
+
end
|
26
|
+
root_module.ui.debug { "[Librarian] #{yield} [#{loc}]" }
|
27
|
+
else
|
28
|
+
root_module.ui.debug { "[Librarian] #{yield}" }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'librarian/helpers/debug'
|
2
|
+
|
3
|
+
require 'librarian/lockfile/compiler'
|
4
|
+
require 'librarian/lockfile/parser'
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
class Lockfile
|
8
|
+
|
9
|
+
include Helpers::Debug
|
10
|
+
|
11
|
+
attr_reader :root_module, :path
|
12
|
+
|
13
|
+
def initialize(root_module, path)
|
14
|
+
@root_module = root_module
|
15
|
+
@path = path
|
16
|
+
end
|
17
|
+
|
18
|
+
def save(resolution)
|
19
|
+
Compiler.new(root_module).compile(resolution)
|
20
|
+
end
|
21
|
+
|
22
|
+
def load(string)
|
23
|
+
Parser.new(root_module).parse(string)
|
24
|
+
end
|
25
|
+
|
26
|
+
def read
|
27
|
+
load(path.read)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'librarian/helpers/debug'
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
class Lockfile
|
5
|
+
class Compiler
|
6
|
+
|
7
|
+
include Helpers::Debug
|
8
|
+
|
9
|
+
attr_reader :root_module
|
10
|
+
|
11
|
+
def initialize(root_module)
|
12
|
+
@root_module = root_module
|
13
|
+
end
|
14
|
+
|
15
|
+
def compile(resolution)
|
16
|
+
out = StringIO.new
|
17
|
+
save_sources(out, resolution.manifests)
|
18
|
+
save_dependencies(out, resolution.dependencies)
|
19
|
+
out.string
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def save_sources(out, manifests)
|
25
|
+
dsl_class.source_types.map{|t| t[1]}.each do |type|
|
26
|
+
type_manifests = manifests.select{|m| type === m.source}
|
27
|
+
sources = type_manifests.map{|m| m.source}.uniq.sort_by{|s| s.to_s}
|
28
|
+
sources.each do |source|
|
29
|
+
source_manifests = type_manifests.select{|m| source == m.source}
|
30
|
+
save_source(out, source, source_manifests)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def save_source(out, source, manifests)
|
36
|
+
out.puts "#{source.class.lock_name}"
|
37
|
+
options = source.to_lock_options
|
38
|
+
remote = options.delete(:remote)
|
39
|
+
out.puts " remote: #{remote}"
|
40
|
+
options.to_a.sort_by{|a| a[0].to_s}.each do |o|
|
41
|
+
out.puts " #{o[0]}: #{o[1]}"
|
42
|
+
end
|
43
|
+
out.puts " specs:"
|
44
|
+
manifests.sort_by{|a| a.name}.each do |manifest|
|
45
|
+
out.puts " #{manifest.name} (#{manifest.version})"
|
46
|
+
manifest.dependencies.sort_by{|a| a.name}.each do |dependency|
|
47
|
+
out.puts " #{dependency.name} (#{dependency.requirement})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
out.puts ""
|
51
|
+
end
|
52
|
+
|
53
|
+
def save_dependencies(out, dependencies)
|
54
|
+
out.puts "DEPENDENCIES"
|
55
|
+
dependencies.sort_by{|a| a.name}.each do |d|
|
56
|
+
res = "#{d.name}"
|
57
|
+
res << " (#{d.requirement})" if d.requirement
|
58
|
+
out.puts " #{res}"
|
59
|
+
end
|
60
|
+
out.puts ""
|
61
|
+
end
|
62
|
+
|
63
|
+
def dsl_class
|
64
|
+
root_module.dsl_class
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|