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.
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