librarian-puppet-rethinc 3.0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.md +331 -0
- data/bin/librarian-puppet +7 -0
- data/lib/librarian/puppet/action/install.rb +26 -0
- data/lib/librarian/puppet/action/resolve.rb +26 -0
- data/lib/librarian/puppet/action.rb +2 -0
- data/lib/librarian/puppet/cli.rb +117 -0
- data/lib/librarian/puppet/dependency.rb +26 -0
- data/lib/librarian/puppet/dsl.rb +123 -0
- data/lib/librarian/puppet/environment.rb +66 -0
- data/lib/librarian/puppet/extension.rb +9 -0
- data/lib/librarian/puppet/lockfile.rb +78 -0
- data/lib/librarian/puppet/resolver.rb +21 -0
- data/lib/librarian/puppet/rethinc.rb +1 -0
- data/lib/librarian/puppet/source/forge/repo.rb +158 -0
- data/lib/librarian/puppet/source/forge/repo_v1.rb +92 -0
- data/lib/librarian/puppet/source/forge/repo_v3.rb +100 -0
- data/lib/librarian/puppet/source/forge.rb +180 -0
- data/lib/librarian/puppet/source/git.rb +74 -0
- data/lib/librarian/puppet/source/githubtarball/repo.rb +172 -0
- data/lib/librarian/puppet/source/githubtarball.rb +137 -0
- data/lib/librarian/puppet/source/local.rb +176 -0
- data/lib/librarian/puppet/source/path.rb +12 -0
- data/lib/librarian/puppet/source/repo.rb +46 -0
- data/lib/librarian/puppet/source.rb +4 -0
- data/lib/librarian/puppet/templates/Puppetfile +25 -0
- data/lib/librarian/puppet/util.rb +78 -0
- data/lib/librarian/puppet/version.rb +5 -0
- data/lib/librarian/puppet.rb +25 -0
- metadata +242 -0
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'librarian/puppet/util'
|
3
|
+
require 'librarian/puppet/source/forge/repo_v1'
|
4
|
+
require 'librarian/puppet/source/forge/repo_v3'
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
module Puppet
|
8
|
+
module Source
|
9
|
+
class Forge
|
10
|
+
include Librarian::Puppet::Util
|
11
|
+
|
12
|
+
class << self
|
13
|
+
LOCK_NAME = 'FORGE'
|
14
|
+
|
15
|
+
def default=(source)
|
16
|
+
@@default = source
|
17
|
+
end
|
18
|
+
|
19
|
+
def default
|
20
|
+
@@default
|
21
|
+
end
|
22
|
+
|
23
|
+
def lock_name
|
24
|
+
LOCK_NAME
|
25
|
+
end
|
26
|
+
|
27
|
+
def from_lock_options(environment, options)
|
28
|
+
new(environment, options[:remote], options.reject { |k, v| k == :remote })
|
29
|
+
end
|
30
|
+
|
31
|
+
def from_spec_args(environment, uri, options)
|
32
|
+
recognised_options = []
|
33
|
+
unrecognised_options = options.keys - recognised_options
|
34
|
+
unless unrecognised_options.empty?
|
35
|
+
raise Error, "unrecognised options: #{unrecognised_options.join(", ")}"
|
36
|
+
end
|
37
|
+
|
38
|
+
new(environment, uri, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def client_api_version()
|
42
|
+
version = 1
|
43
|
+
pe_version = Librarian::Puppet.puppet_version.match(/\(Puppet Enterprise (.+)\)/)
|
44
|
+
|
45
|
+
# Puppet 3.6.0+ uses api v3
|
46
|
+
if Librarian::Puppet::puppet_gem_version >= Gem::Version.create('3.6.0.a')
|
47
|
+
version = 3
|
48
|
+
# Puppet enterprise 3.2.0+ uses api v3
|
49
|
+
elsif pe_version and Gem::Version.create(pe_version[1].strip) >= Gem::Version.create('3.2.0')
|
50
|
+
version = 3
|
51
|
+
end
|
52
|
+
return version
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_accessor :environment
|
58
|
+
private :environment=
|
59
|
+
attr_reader :uri
|
60
|
+
|
61
|
+
def initialize(environment, uri, options = {})
|
62
|
+
self.environment = environment
|
63
|
+
|
64
|
+
if uri =~ %r{^http(s)?://forge\.puppetlabs\.com}
|
65
|
+
uri = "https://forgeapi.puppetlabs.com"
|
66
|
+
warn { "Replacing Puppet Forge API URL to use v3 #{uri}. You should update your Puppetfile" }
|
67
|
+
end
|
68
|
+
|
69
|
+
@uri = URI::parse(uri)
|
70
|
+
@cache_path = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_s
|
74
|
+
clean_uri(uri).to_s
|
75
|
+
end
|
76
|
+
|
77
|
+
def ==(other)
|
78
|
+
other &&
|
79
|
+
self.class == other.class &&
|
80
|
+
self.uri == other.uri
|
81
|
+
end
|
82
|
+
|
83
|
+
alias :eql? :==
|
84
|
+
|
85
|
+
def hash
|
86
|
+
self.to_s.hash
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_spec_args
|
90
|
+
[clean_uri(uri).to_s, {}]
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_lock_options
|
94
|
+
{:remote => uri.to_s}
|
95
|
+
end
|
96
|
+
|
97
|
+
def pinned?
|
98
|
+
false
|
99
|
+
end
|
100
|
+
|
101
|
+
def unpin!
|
102
|
+
end
|
103
|
+
|
104
|
+
def install!(manifest)
|
105
|
+
manifest.source == self or raise ArgumentError
|
106
|
+
|
107
|
+
debug { "Installing #{manifest}" }
|
108
|
+
|
109
|
+
name = manifest.name
|
110
|
+
version = manifest.version
|
111
|
+
install_path = install_path(name)
|
112
|
+
repo = repo(name)
|
113
|
+
|
114
|
+
repo.install_version! version, install_path
|
115
|
+
end
|
116
|
+
|
117
|
+
def manifest(name, version, dependencies)
|
118
|
+
manifest = Manifest.new(self, name)
|
119
|
+
manifest.version = version
|
120
|
+
manifest.dependencies = dependencies
|
121
|
+
manifest
|
122
|
+
end
|
123
|
+
|
124
|
+
def cache_path
|
125
|
+
@cache_path ||= begin
|
126
|
+
if environment.use_short_cache_path
|
127
|
+
# Take only the first 7 digits of the SHA1 checksum of the forge URI
|
128
|
+
# (short Git commit hash approach)
|
129
|
+
dir = Digest::SHA1.hexdigest("#{uri.host}#{uri.path}")[0..6]
|
130
|
+
else
|
131
|
+
dir = "#{uri.host}#{uri.path}".gsub(/[^0-9a-z\-_]/i, '_')
|
132
|
+
end
|
133
|
+
environment.cache_path.join("source/p/f/#{dir}")
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def install_path(name)
|
138
|
+
environment.install_path.join(module_name(name))
|
139
|
+
end
|
140
|
+
|
141
|
+
def fetch_version(name, version_uri)
|
142
|
+
versions = repo(name).versions
|
143
|
+
if versions.include? version_uri
|
144
|
+
version_uri
|
145
|
+
else
|
146
|
+
versions.first
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def fetch_dependencies(name, version, version_uri)
|
151
|
+
repo(name).dependencies(version).map do |k, v|
|
152
|
+
v = Librarian::Dependency::Requirement.new(v).to_gem_requirement
|
153
|
+
Dependency.new(k, v, nil, name)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def manifests(name)
|
158
|
+
repo(name).manifests
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def repo(name)
|
164
|
+
@repo ||= {}
|
165
|
+
|
166
|
+
unless @repo[name]
|
167
|
+
# If we are using the official Forge then use API v3, otherwise use the preferred api
|
168
|
+
# as defined by the CLI option use_v1_api
|
169
|
+
if uri.hostname =~ /\.puppetlabs\.com$/ || !environment.use_v1_api
|
170
|
+
@repo[name] = RepoV3.new(self, name)
|
171
|
+
else
|
172
|
+
@repo[name] = RepoV1.new(self, name)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
@repo[name]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'librarian/source/git'
|
2
|
+
require 'librarian/puppet/source/local'
|
3
|
+
|
4
|
+
module Librarian
|
5
|
+
module Source
|
6
|
+
class Git
|
7
|
+
class Repository
|
8
|
+
def hash_from(remote, reference)
|
9
|
+
branch_names = remote_branch_names[remote]
|
10
|
+
if branch_names.include?(reference)
|
11
|
+
reference = "#{remote}/#{reference}"
|
12
|
+
end
|
13
|
+
|
14
|
+
command = %W(rev-parse #{reference}^{commit} --quiet)
|
15
|
+
run!(command, :chdir => true).strip
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Puppet
|
22
|
+
module Source
|
23
|
+
class Git < Librarian::Source::Git
|
24
|
+
include Local
|
25
|
+
include Librarian::Puppet::Util
|
26
|
+
|
27
|
+
def cache!
|
28
|
+
return vendor_checkout! if vendor_cached?
|
29
|
+
|
30
|
+
if environment.local?
|
31
|
+
raise Error, "Could not find a local copy of #{uri}#{" at #{sha}" unless sha.nil?}."
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
super
|
36
|
+
rescue Librarian::Posix::CommandFailure => e
|
37
|
+
raise Error, "Could not checkout #{uri}#{" at #{sha}" unless sha.nil?}: #{e}"
|
38
|
+
end
|
39
|
+
|
40
|
+
cache_in_vendor(repository.path) if environment.vendor?
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def vendor_tar
|
46
|
+
environment.vendor_source.join("#{sha}.tar")
|
47
|
+
end
|
48
|
+
|
49
|
+
def vendor_tgz
|
50
|
+
environment.vendor_source.join("#{sha}.tar.gz")
|
51
|
+
end
|
52
|
+
|
53
|
+
def vendor_cached?
|
54
|
+
vendor_tgz.exist?
|
55
|
+
end
|
56
|
+
|
57
|
+
def vendor_checkout!
|
58
|
+
repository.path.rmtree if repository.path.exist?
|
59
|
+
repository.path.mkpath
|
60
|
+
|
61
|
+
Librarian::Posix.run!(%W{tar xzf #{vendor_tgz}}, :chdir => repository.path.to_s)
|
62
|
+
|
63
|
+
repository_cached!
|
64
|
+
end
|
65
|
+
|
66
|
+
def cache_in_vendor(tmp_path)
|
67
|
+
Librarian::Posix.run!(%W{git archive -o #{vendor_tar} #{sha}}, :chdir => tmp_path.to_s)
|
68
|
+
Librarian::Posix.run!(%W{gzip #{vendor_tar}}, :chdir => tmp_path.to_s)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/https'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
require 'librarian/puppet/version'
|
7
|
+
require 'librarian/puppet/source/repo'
|
8
|
+
|
9
|
+
module Librarian
|
10
|
+
module Puppet
|
11
|
+
module Source
|
12
|
+
class GitHubTarball
|
13
|
+
class Repo < Librarian::Puppet::Source::Repo
|
14
|
+
include Librarian::Puppet::Util
|
15
|
+
|
16
|
+
TOKEN_KEY = 'GITHUB_API_TOKEN'
|
17
|
+
|
18
|
+
def versions
|
19
|
+
return @versions if @versions
|
20
|
+
data = api_call("/repos/#{source.uri}/tags")
|
21
|
+
if data.nil?
|
22
|
+
raise Error, "Unable to find module '#{source.uri}' on https://github.com"
|
23
|
+
end
|
24
|
+
|
25
|
+
all_versions = data.map { |r| r['name'].gsub(/^v/, '') }.sort.reverse
|
26
|
+
|
27
|
+
all_versions.delete_if do |version|
|
28
|
+
version !~ /\A\d+\.\d+(\.\d+.*)?\z/
|
29
|
+
end
|
30
|
+
|
31
|
+
@versions = all_versions.compact
|
32
|
+
debug { " Module #{name} found versions: #{@versions.join(", ")}" }
|
33
|
+
@versions
|
34
|
+
end
|
35
|
+
|
36
|
+
def manifests
|
37
|
+
versions.map do |version|
|
38
|
+
Manifest.new(source, name, version)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def install_version!(version, install_path)
|
43
|
+
if environment.local? && !vendored?(vendored_name, version)
|
44
|
+
raise Error, "Could not find a local copy of #{source.uri} at #{version}."
|
45
|
+
end
|
46
|
+
|
47
|
+
vendor_cache(source.uri.to_s, version) unless vendored?(vendored_name, version)
|
48
|
+
|
49
|
+
cache_version_unpacked! version
|
50
|
+
|
51
|
+
if install_path.exist? && rsync? != true
|
52
|
+
install_path.rmtree
|
53
|
+
end
|
54
|
+
|
55
|
+
unpacked_path = version_unpacked_cache_path(version).children.first
|
56
|
+
cp_r(unpacked_path, install_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def cache_version_unpacked!(version)
|
60
|
+
path = version_unpacked_cache_path(version)
|
61
|
+
return if path.directory?
|
62
|
+
|
63
|
+
path.mkpath
|
64
|
+
|
65
|
+
target = vendored?(vendored_name, version) ? vendored_path(vendored_name, version) : name
|
66
|
+
|
67
|
+
Librarian::Posix.run!(%W{tar xzf #{target} -C #{path}})
|
68
|
+
end
|
69
|
+
|
70
|
+
def vendor_cache(name, version)
|
71
|
+
clean_up_old_cached_versions(vendored_name(name))
|
72
|
+
|
73
|
+
url = "https://api.github.com/repos/#{name}/tarball/#{version}"
|
74
|
+
add_api_token_to_url(url)
|
75
|
+
|
76
|
+
environment.vendor!
|
77
|
+
File.open(vendored_path(vendored_name(name), version).to_s, 'wb') do |f|
|
78
|
+
begin
|
79
|
+
debug { "Downloading <#{url}> to <#{f.path}>" }
|
80
|
+
URI.open(url,
|
81
|
+
"User-Agent" => "librarian-puppet v#{Librarian::Puppet::VERSION}") do |res|
|
82
|
+
while buffer = res.read(8192)
|
83
|
+
f.write(buffer)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
rescue OpenURI::HTTPError => e
|
87
|
+
raise e, "Error requesting <#{url}>: #{e.to_s}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def clean_up_old_cached_versions(name)
|
93
|
+
Dir["#{environment.vendor_cache}/#{name}*.tar.gz"].each do |old_version|
|
94
|
+
FileUtils.rm old_version
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def token_key_value
|
99
|
+
ENV[TOKEN_KEY]
|
100
|
+
end
|
101
|
+
|
102
|
+
def token_key_nil?
|
103
|
+
token_key_value.nil? || token_key_value.empty?
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_api_token_to_url url
|
107
|
+
if token_key_nil?
|
108
|
+
debug { "#{TOKEN_KEY} environment value is empty or missing" }
|
109
|
+
elsif url.include? "?"
|
110
|
+
url << "&access_token=#{ENV[TOKEN_KEY]}"
|
111
|
+
else
|
112
|
+
url << "?access_token=#{ENV[TOKEN_KEY]}"
|
113
|
+
end
|
114
|
+
url
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def api_call(path)
|
120
|
+
tags = []
|
121
|
+
url = "https://api.github.com#{path}?page=1&per_page=100"
|
122
|
+
while true do
|
123
|
+
debug { " Module #{name} getting tags at: #{url}" }
|
124
|
+
add_api_token_to_url(url)
|
125
|
+
response = http_get(url, :headers => {
|
126
|
+
"User-Agent" => "librarian-puppet v#{Librarian::Puppet::VERSION}"
|
127
|
+
})
|
128
|
+
|
129
|
+
code, data = response.code.to_i, response.body
|
130
|
+
|
131
|
+
if code == 200
|
132
|
+
tags.concat JSON.parse(data)
|
133
|
+
else
|
134
|
+
begin
|
135
|
+
message = JSON.parse(data)['message']
|
136
|
+
if code == 403 && message && message.include?('API rate limit exceeded')
|
137
|
+
raise Error, message + " -- increase limit by authenticating via #{TOKEN_KEY}=your-token"
|
138
|
+
elsif message
|
139
|
+
raise Error, "Error fetching #{url}: [#{code}] #{message}"
|
140
|
+
end
|
141
|
+
rescue JSON::ParserError
|
142
|
+
# response does not return json
|
143
|
+
end
|
144
|
+
raise Error, "Error fetching #{url}: [#{code}] #{response.body}"
|
145
|
+
end
|
146
|
+
|
147
|
+
# next page
|
148
|
+
break if response["link"].nil?
|
149
|
+
next_link = response["link"].split(",").select{|l| l.match /rel=.*next.*/}
|
150
|
+
break if next_link.empty?
|
151
|
+
url = next_link.first.match(/<(.*)>/)[1]
|
152
|
+
end
|
153
|
+
return tags
|
154
|
+
end
|
155
|
+
|
156
|
+
def http_get(url, options)
|
157
|
+
uri = URI.parse(url)
|
158
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
159
|
+
http.use_ssl = true
|
160
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
161
|
+
options[:headers].each { |k, v| request.add_field k, v }
|
162
|
+
http.request(request)
|
163
|
+
end
|
164
|
+
|
165
|
+
def vendored_name(name = source.uri.to_s)
|
166
|
+
name.sub('/','-')
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'librarian/puppet/util'
|
3
|
+
require 'librarian/puppet/source/githubtarball/repo'
|
4
|
+
|
5
|
+
module Librarian
|
6
|
+
module Puppet
|
7
|
+
module Source
|
8
|
+
class GitHubTarball
|
9
|
+
include Librarian::Puppet::Util
|
10
|
+
|
11
|
+
class << self
|
12
|
+
LOCK_NAME = 'GITHUBTARBALL'
|
13
|
+
|
14
|
+
def lock_name
|
15
|
+
LOCK_NAME
|
16
|
+
end
|
17
|
+
|
18
|
+
def from_lock_options(environment, options)
|
19
|
+
new(environment, options[:remote], options.reject { |k, v| k == :remote })
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_spec_args(environment, uri, options)
|
23
|
+
recognised_options = []
|
24
|
+
unrecognised_options = options.keys - recognised_options
|
25
|
+
unless unrecognised_options.empty?
|
26
|
+
raise Error, "unrecognised options: #{unrecognised_options.join(", ")}"
|
27
|
+
end
|
28
|
+
|
29
|
+
new(environment, uri, options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_accessor :environment
|
34
|
+
private :environment=
|
35
|
+
attr_reader :uri
|
36
|
+
|
37
|
+
def initialize(environment, uri, options = {})
|
38
|
+
self.environment = environment
|
39
|
+
@uri = URI::parse(uri)
|
40
|
+
@cache_path = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
clean_uri(uri).to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
other &&
|
49
|
+
self.class == other.class &&
|
50
|
+
self.uri == other.uri
|
51
|
+
end
|
52
|
+
|
53
|
+
alias :eql? :==
|
54
|
+
|
55
|
+
def hash
|
56
|
+
self.to_s.hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_spec_args
|
60
|
+
[clean_uri(uri).to_s, {}]
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_lock_options
|
64
|
+
{:remote => clean_uri(uri).to_s}
|
65
|
+
end
|
66
|
+
|
67
|
+
def pinned?
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
def unpin!
|
72
|
+
end
|
73
|
+
|
74
|
+
def install!(manifest)
|
75
|
+
manifest.source == self or raise ArgumentError
|
76
|
+
|
77
|
+
debug { "Installing #{manifest}" }
|
78
|
+
|
79
|
+
name = manifest.name
|
80
|
+
version = manifest.version
|
81
|
+
install_path = install_path(name)
|
82
|
+
repo = repo(name)
|
83
|
+
|
84
|
+
repo.install_version! version, install_path
|
85
|
+
end
|
86
|
+
|
87
|
+
def manifest(name, version, dependencies)
|
88
|
+
manifest = Manifest.new(self, name)
|
89
|
+
manifest.version = version
|
90
|
+
manifest.dependencies = dependencies
|
91
|
+
manifest
|
92
|
+
end
|
93
|
+
|
94
|
+
def cache_path
|
95
|
+
@cache_path ||= begin
|
96
|
+
if environment.use_short_cache_path
|
97
|
+
# Take only the first 7 digits of the SHA1 checksum of the forge URI
|
98
|
+
# (short Git commit hash approach)
|
99
|
+
dir = Digest::SHA1.hexdigest("#{uri.host}#{uri.path}")[0..6]
|
100
|
+
else
|
101
|
+
dir = "#{uri.host}#{uri.path}"
|
102
|
+
end
|
103
|
+
environment.cache_path.join("source/p/gh/#{dir}")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def install_path(name)
|
108
|
+
environment.install_path.join(module_name(name))
|
109
|
+
end
|
110
|
+
|
111
|
+
def fetch_version(name, version_uri)
|
112
|
+
versions = repo(name).versions
|
113
|
+
if versions.include? version_uri
|
114
|
+
version_uri
|
115
|
+
else
|
116
|
+
versions.first
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def fetch_dependencies(name, version, version_uri)
|
121
|
+
{}
|
122
|
+
end
|
123
|
+
|
124
|
+
def manifests(name)
|
125
|
+
repo(name).manifests
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def repo(name)
|
131
|
+
@repo ||= {}
|
132
|
+
@repo[name] ||= Repo.new(self, name)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|