librarian 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.gitignore +5 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +46 -0
  6. data/Rakefile +13 -0
  7. data/bin/librarian-chef +7 -0
  8. data/bin/librarian-mock +7 -0
  9. data/config/cucumber.yaml +1 -0
  10. data/features/chef/cli/install.feature +69 -0
  11. data/features/support/env.rb +5 -0
  12. data/lib/librarian.rb +191 -0
  13. data/lib/librarian/chef.rb +1 -0
  14. data/lib/librarian/chef/cli.rb +14 -0
  15. data/lib/librarian/chef/dsl.rb +14 -0
  16. data/lib/librarian/chef/extension.rb +24 -0
  17. data/lib/librarian/chef/manifest.rb +43 -0
  18. data/lib/librarian/chef/particularity.rb +9 -0
  19. data/lib/librarian/chef/source.rb +3 -0
  20. data/lib/librarian/chef/source/git.rb +14 -0
  21. data/lib/librarian/chef/source/local.rb +80 -0
  22. data/lib/librarian/chef/source/path.rb +14 -0
  23. data/lib/librarian/chef/source/site.rb +271 -0
  24. data/lib/librarian/cli.rb +76 -0
  25. data/lib/librarian/dependency.rb +44 -0
  26. data/lib/librarian/dsl.rb +76 -0
  27. data/lib/librarian/dsl/receiver.rb +46 -0
  28. data/lib/librarian/dsl/target.rb +164 -0
  29. data/lib/librarian/helpers.rb +13 -0
  30. data/lib/librarian/helpers/debug.rb +35 -0
  31. data/lib/librarian/lockfile.rb +31 -0
  32. data/lib/librarian/lockfile/compiler.rb +69 -0
  33. data/lib/librarian/lockfile/parser.rb +102 -0
  34. data/lib/librarian/manifest.rb +88 -0
  35. data/lib/librarian/manifest_set.rb +131 -0
  36. data/lib/librarian/mock.rb +1 -0
  37. data/lib/librarian/mock/cli.rb +14 -0
  38. data/lib/librarian/mock/dsl.rb +14 -0
  39. data/lib/librarian/mock/extension.rb +28 -0
  40. data/lib/librarian/mock/particularity.rb +7 -0
  41. data/lib/librarian/mock/source.rb +1 -0
  42. data/lib/librarian/mock/source/mock.rb +88 -0
  43. data/lib/librarian/mock/source/mock/registry.rb +79 -0
  44. data/lib/librarian/particularity.rb +7 -0
  45. data/lib/librarian/resolution.rb +36 -0
  46. data/lib/librarian/resolver.rb +139 -0
  47. data/lib/librarian/source.rb +2 -0
  48. data/lib/librarian/source/git.rb +91 -0
  49. data/lib/librarian/source/git/repository.rb +82 -0
  50. data/lib/librarian/source/local.rb +33 -0
  51. data/lib/librarian/source/path.rb +52 -0
  52. data/lib/librarian/spec.rb +11 -0
  53. data/lib/librarian/spec_change_set.rb +169 -0
  54. data/lib/librarian/specfile.rb +16 -0
  55. data/lib/librarian/support/abstract_method.rb +21 -0
  56. data/lib/librarian/ui.rb +64 -0
  57. data/lib/librarian/version.rb +3 -0
  58. data/librarian.gemspec +29 -0
  59. data/spec/chef/git_source_spec.rb +93 -0
  60. data/spec/dsl_spec.rb +167 -0
  61. data/spec/lockfile_spec.rb +44 -0
  62. data/spec/meta/requires_spec.rb +27 -0
  63. data/spec/resolver_spec.rb +172 -0
  64. data/spec/spec_change_set_spec.rb +165 -0
  65. 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,14 @@
1
+ require 'librarian/cli'
2
+ require 'librarian/mock'
3
+ require 'librarian/mock/particularity'
4
+
5
+ module Librarian
6
+ module Mock
7
+ class Cli < Librarian::Cli
8
+
9
+ include Particularity
10
+ extend Particularity
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'librarian/dsl'
2
+ require 'librarian/mock/source'
3
+
4
+ module Librarian
5
+ module Mock
6
+ class Dsl < Librarian::Dsl
7
+ dependency :dep
8
+
9
+ source :src => Source::Mock
10
+
11
+ shortcut :a, :src => 'source-a'
12
+ end
13
+ end
14
+ end
@@ -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,7 @@
1
+ module Librarian
2
+ module Particularity
3
+ def root_module
4
+ Mock
5
+ end
6
+ end
7
+ 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