Skip to content

Commit d09a3af

Browse files
authored
Merge pull request #2125 from NicholasBHubbard/fix-perl-module-provides
Properly find CPAN provided modules
2 parents 057b732 + 3ec5eb0 commit d09a3af

2 files changed

Lines changed: 89 additions & 18 deletions

File tree

lib/fpm/package/cpan.rb

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def input(package)
6060
moduledir = package
6161
result = {}
6262
else
63-
result = search(package)
63+
result = search_module(package, version)
6464
tarball = download(result, version)
6565
moduledir = unpack(tarball)
6666
end
@@ -98,20 +98,17 @@ def input(package)
9898
else; metadata["license"]
9999
end
100100

101-
unless metadata["distribution"].nil?
101+
unless metadata["release"].nil?
102+
dist_name, _, dist_version = metadata["release"].rpartition('-')
102103
logger.info("Setting package name from 'distribution'",
103-
:distribution => metadata["distribution"])
104-
self.name = fix_name(metadata["distribution"])
104+
:distribution => dist_name)
105+
self.name = fix_name(dist_name)
106+
self.provides = search_provided_modules(dist_name, dist_version)
105107
else
106108
logger.info("Setting package name from 'name'",
107109
:name => metadata["name"])
108110
self.name = fix_name(metadata["name"])
109-
end
110-
111-
unless metadata["module"].nil?
112-
metadata["module"].each do |m|
113-
self.provides << cap_name(m["name"]) + " = #{self.version}"
114-
end
111+
self.provides << cap_name(metadata["name"]) + " = #{self.version}"
115112
end
116113

117114
# author is not always set or it may be a string instead of an array
@@ -370,26 +367,82 @@ def download(metadata, cpan_version=nil)
370367
return build_path(tarball)
371368
end # def download
372369

373-
def search(package)
374-
logger.info("Asking metacpan about a module", :module => package)
375-
metacpan_url = "https://fastapi.metacpan.org/v1/module/" + package
370+
def search_module(module_name, version=nil)
371+
logger.info("Asking metacpan about a module", :module => module_name, :version => version)
372+
metacpan_api_url = "https://fastapi.metacpan.org/v1/module/_search"
373+
metacpan_api_query = <<-EOS
374+
{
375+
"query": {
376+
"bool": {
377+
"must": [
378+
{ "term": { "module.name": "#{module_name}" } },
379+
{ "term": { "maturity": "released" } },
380+
{ "term": { #{version.nil? ? '"status": "latest"' : '"module.version": "' + version + '"'} } }
381+
]
382+
}
383+
},
384+
"size": 1
385+
}
386+
EOS
376387
begin
377-
response = httpfetch(metacpan_url)
388+
response = httppost(metacpan_api_url, metacpan_api_query)
378389
rescue Net::HTTPServerException => e
379390
#logger.error("metacpan query failed.", :error => response.status_line,
380391
#:module => package, :url => metacpan_url)
381392
logger.error("metacpan query failed.", :error => e.message,
382-
:module => package, :url => metacpan_url)
393+
:module => module_name, :version => version, :url => metacpan_api_url)
383394
raise FPM::InvalidPackageConfiguration, "metacpan query failed"
384395
end
385396

386397
#data = ""
387398
#response.read_body { |c| p c; data << c }
388399
data = response.body
389-
metadata = JSON.parse(data)
400+
metadata = JSON.parse(data)['hits']['hits'][0]['_source']
390401
return metadata
391402
end # def search
392403

404+
def search_provided_modules(distribution, version)
405+
logger.info("Asking metacpan about a releases provided modules",
406+
:distribution => distribution,
407+
:version => version)
408+
metacpan_api_url = "https://fastapi.metacpan.org/v1/module/_search?_source=module.name,module.version"
409+
metacpan_api_query = <<-EOS
410+
{
411+
"query": {
412+
"bool": {
413+
"filter": [
414+
{ "exists": { "field": "module" } },
415+
{ "term": { "release": "#{distribution}-#{version}" } }
416+
]
417+
}
418+
},
419+
"sort": [ { "module.name": { "order": "asc" } } ],
420+
"size": 5000
421+
}
422+
EOS
423+
424+
begin
425+
response = httppost(metacpan_api_url, metacpan_api_query)
426+
rescue Net::HTTPServerException => e
427+
logger.error("metacpan release query failed.", :error => e.message,
428+
:url => metacpan_api_url)
429+
raise FPM::InvalidPackageConfiguration, "metacpan release query failed"
430+
end
431+
query_hits = JSON.parse(response.body)['hits']['hits']
432+
433+
provided_modules = []
434+
query_hits.each do |h|
435+
h["_source"]["module"].each do |m|
436+
module_name = m["name"]
437+
module_version = m["version"]
438+
Array(module_name).zip(Array(module_version)).each do |name, version|
439+
provided_modules << cap_name(name) + (version ? " = #{version}" : "")
440+
end
441+
end
442+
end
443+
return provided_modules
444+
end # def search_provided_modules
445+
393446
def cap_name(name)
394447
return "perl(" + name.gsub("-", "::") + ")"
395448
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 package Regexp::Common" do

0 commit comments

Comments
 (0)