Skip to content

Commit 541b5a4

Browse files
Properly find CPAN provided modules
1 parent 088be70 commit 541b5a4

File tree

2 files changed

+87
-19
lines changed

2 files changed

+87
-19
lines changed

lib/fpm/package/cpan.rb

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def input(package)
5353
moduledir = package
5454
result = {}
5555
else
56-
result = search(package)
56+
result = search_module(package, version)
5757
tarball = download(result, version)
5858
moduledir = unpack(tarball)
5959
end
@@ -91,20 +91,17 @@ def input(package)
9191
else; metadata["license"]
9292
end
9393

94-
unless metadata["distribution"].nil?
94+
unless metadata["release"].nil?
95+
dist_name, _, dist_version = metadata["release"].rpartition('-')
9596
logger.info("Setting package name from 'distribution'",
96-
:distribution => metadata["distribution"])
97-
self.name = fix_name(metadata["distribution"])
97+
:distribution => dist_name)
98+
self.name = fix_name(dist_name)
99+
self.provides = search_provided_modules(dist_name, dist_version)
98100
else
99101
logger.info("Setting package name from 'name'",
100102
:name => metadata["name"])
101103
self.name = fix_name(metadata["name"])
102-
end
103-
104-
unless metadata["module"].nil?
105-
metadata["module"].each do |m|
106-
self.provides << cap_name(m["name"]) + " = #{self.version}"
107-
end
104+
self.provides << cap_name(metadata["name"]) + " = #{self.version}"
108105
end
109106

110107
# author is not always set or it may be a string instead of an array
@@ -167,7 +164,6 @@ def input(package)
167164
self.dependencies << "#{dep_name} >= #{version}"
168165
next
169166
end
170-
dep = search(dep_name)
171167

172168
name = cap_name(dep_name)
173169

@@ -364,26 +360,80 @@ def download(metadata, cpan_version=nil)
364360
return build_path(tarball)
365361
end # def download
366362

367-
def search(package)
368-
logger.info("Asking metacpan about a module", :module => package)
369-
metacpan_url = "https://fastapi.metacpan.org/v1/module/" + package
363+
def search_module(module_name, version=nil)
364+
logger.info("Asking metacpan about a module", :module => module_name, :version => version)
365+
metacpan_api_url = "https://fastapi.metacpan.org/v1/module/_search"
366+
metacpan_api_query = <<-EOS
367+
{
368+
"query": {
369+
"bool": {
370+
"must": [
371+
{ "term": { "module.name": "#{module_name}" } },
372+
{ "term": { "maturity": "released" } },
373+
{ "term": { #{version.nil? ? '"status": "latest"' : '"module.version": "' + version + '"'} } }
374+
]
375+
}
376+
},
377+
"size": 1
378+
}
379+
EOS
370380
begin
371-
response = httpfetch(metacpan_url)
381+
response = httppost(metacpan_api_url, metacpan_api_query)
372382
rescue Net::HTTPServerException => e
373383
#logger.error("metacpan query failed.", :error => response.status_line,
374384
#:module => package, :url => metacpan_url)
375385
logger.error("metacpan query failed.", :error => e.message,
376-
:module => package, :url => metacpan_url)
386+
:module => module_name, :version => version, :url => metacpan_api_url)
377387
raise FPM::InvalidPackageConfiguration, "metacpan query failed"
378388
end
379389

380390
#data = ""
381391
#response.read_body { |c| p c; data << c }
382392
data = response.body
383-
metadata = JSON.parse(data)
393+
metadata = JSON.parse(data)['hits']['hits'][0]['_source']
384394
return metadata
385395
end # def search
386396

397+
def search_provided_modules(distribution, version)
398+
logger.info("Asking metacpan about a releases provided modules",
399+
:distribution => distribution,
400+
:version => version)
401+
metacpan_api_url = "https://fastapi.metacpan.org/v1/module/_search"
402+
metacpan_api_query = <<-EOS
403+
{
404+
"query": {
405+
"bool": {
406+
"filter": [
407+
{ "exists": { "field": "module" } },
408+
{ "term": { "release": "#{distribution}-#{version}" } }
409+
]
410+
}
411+
},
412+
"fields": ["module.name", "module.version"],
413+
"sort": [ { "module.name": { "order": "asc" } } ],
414+
"size": 5000
415+
}
416+
EOS
417+
begin
418+
response = httppost(metacpan_api_url, metacpan_api_query)
419+
rescue Net::HTTPServerException => e
420+
logger.error("metacpan release query failed.", :error => e.message,
421+
:url => metacpan_api_url)
422+
raise FPM::InvalidPackageConfiguration, "metacpan release query failed"
423+
end
424+
query_hits = JSON.parse(response.body)['hits']['hits']
425+
426+
provided_modules = []
427+
query_hits.each do |m|
428+
module_name = m["fields"]["module.name"]
429+
module_version = m["fields"]["module.version"]
430+
Array(module_name).zip(Array(module_version)).each do |name, version|
431+
provided_modules << cap_name(name) + (version ? " = #{version}" : "")
432+
end
433+
end
434+
return provided_modules
435+
end # def search_provided_modules
436+
387437
def cap_name(name)
388438
return "perl(" + name.gsub("-", "::") + ")"
389439
end # def cap_name

spec/fpm/package/cpan_spec.rb

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,34 @@
4343
end
4444

4545
it "should return metadata hash" do
46-
metadata = subject.instance_eval { search("File::Temp") }
46+
metadata = subject.instance_eval { search_module("File::Temp") }
4747
insist { metadata.class } == Hash
4848
insist { metadata["name"] } == "Temp.pm"
4949
insist { metadata["distribution"] } == "File-Temp"
5050
end
5151

5252
it "should download precise version" do
53-
metadata = subject.instance_eval { search("Set::Tiny") }
53+
metadata = subject.instance_eval { search_module("Set::Tiny") }
5454
insist { File.basename(subject.instance_eval { download(metadata, "0.01") }) } == "Set-Tiny-0.01.tar.gz"
5555
end
5656

57+
it "should find distributions provided modules" do
58+
provided_modules = subject.instance_eval { search_provided_modules("Test-DB", "0.10") }
59+
insist { provided_modules } == ["perl(Test::DB) = 0.10", "perl(Test::DB::Mssql) = 0.10", "perl(Test::DB::Mysql) = 0.10", "perl(Test::DB::Postgres) = 0.10", "perl(Test::DB::Sqlite) = 0.10"]
60+
61+
# The Set-Tiny-0.01 release provides a single module, Set::Tiny version 0.01
62+
provided_modules = subject.instance_eval { search_provided_modules("Set-Tiny", "0.01") }
63+
insist { provided_modules } == ["perl(Set::Tiny) = 0.01"]
64+
65+
# Class::DI has packages with no version
66+
provided_modules = subject.instance_eval { search_provided_modules("Class-DI", "0.03") }
67+
insist { provided_modules } == ["perl(Class::DI) = 0.03", "perl(Class::DI::Definition)", "perl(Class::DI::Factory)", "perl(Class::DI::Resource)", "perl(Class::DI::Resource::YAML)"]
68+
69+
# File::Spec is a module provided by the PathTools distribution
70+
provided_modules = subject.instance_eval { search_provided_modules("File-Spec", "3.75") }
71+
insist { provided_modules } == []
72+
end
73+
5774
it "should package Digest::MD5" do
5875
# Set the version explicitly because we default to installing the newest
5976
# version, and a new version could be released that breaks the test.
@@ -71,6 +88,7 @@
7188
insist { subject.description } == "Perl interface to the MD-5 algorithm"
7289
insist { subject.vendor } == "Gisle Aas <gisle@activestate.com>"
7390
insist { subject.dependencies.sort } == ["perl >= 5.006", "perl(Digest::base) >= 1.00", "perl(XSLoader)"]
91+
insist { subject.provides } == ["perl(Digest::MD5) = 2.58"]
7492
end
7593

7694
it "should unpack tarball containing ./ leading paths" do

0 commit comments

Comments
 (0)